humanforge 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 humanforge contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,231 @@
1
- # Temporary Holding Version
1
+ # HumanForge
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ An open-source 3D avatar SDK for React and React Native, built as an open alternative to hosted
4
+ avatar builders such as MetaPerson Creator. It has two modes:
5
+
6
+ | | **Realistic human** (`humanforge/human`) | **Stylized low-poly** (`humanforge/core`) |
7
+ | --- | --- | --- |
8
+ | Look | Photoreal skin, eyes, hair cards, clothes | Flat-colored, procedural |
9
+ | Base | MakeHuman / MPFB2 human (CC0), 26.7k-triangle body | three.js primitives, 7–11k triangles |
10
+ | Customization | 11 macro + 57 detail morph sliders, 20 skins, 9 eye colors, 12 brows, 4 lashes, 10 hairstyles, 18 outfits and shoes | 30+ options |
11
+ | Rig | 52-bone **Mixamo** skeleton with skin weights (clothes and hair follow) | none |
12
+ | Face animation | 34 FACS blendshapes with **ARKit names** (`jawOpen`, `mouthSmile`, `eyeBlinkLeft`, …) | none |
13
+ | Download | ~1.4 MB one-time core + ~0.3–0.8 MB per look (gzipped, cached on device) | 0 (code only) |
14
+ | Export | Compressed GLB (~3 MB with textures, sparse blendshapes) | GLB ~160 KB |
15
+
16
+ ## Realistic human
17
+
18
+ ### 1. Build the asset package (once)
19
+
20
+ The human is built from CC0 data: the MakeHuman base mesh, targets, skins, hair and clothes, plus
21
+ MPFB2's Mixamo rig and expression units.
22
+
23
+ ```bash
24
+ git clone --depth 1 --filter=blob:none --sparse https://github.com/makehumancommunity/mpfb2.git
25
+ cd mpfb2 && git sparse-checkout set src/mpfb/data && cd ..
26
+ curl -LO https://files2.makehumancommunity.org/asset_packs/makehuman_system_assets/makehuman_system_assets_cc0.zip
27
+ unzip makehuman_system_assets_cc0.zip -d mh_assets
28
+ npm run build:human -- mpfb2/src/mpfb/data mh_assets # -> assets/human (~14 MB, all options)
29
+ ```
30
+
31
+ Host `assets/human/` on any static host or CDN (S3, Cloudflare R2, GitHub Pages…). No server
32
+ compression is needed: binaries are pre-gzipped and decoded in JS by `fflate`.
33
+
34
+ ### 2. React Native (Expo)
35
+
36
+ ```bash
37
+ npx expo install expo-gl expo-file-system three fflate
38
+ ```
39
+
40
+ ```tsx
41
+ import { HumanView, useHumanPackage } from 'humanforge';
42
+
43
+ const { pkg } = useHumanPackage('https://cdn.example.com/human');
44
+ if (!pkg) return <Loading />;
45
+
46
+ <HumanView
47
+ pkg={pkg}
48
+ framing="face" // or "body"
49
+ textureSize="1k" // "2k" for the editor screen
50
+ config={{ gender: 0.1, age: 0.2, african: 1, asian: 0, caucasian: 0,
51
+ skin: 'young_african_female', hair: 'afro01', hairColor: '#1a120c',
52
+ detail: { noseWidth: 0.3, eyeSize: 0.2 },
53
+ clothes: ['female_casualsuit01', 'shoes01'] }}
54
+ expressions={{ mouthSmile: 0.6 }}
55
+ style={{ width: 240, height: 240, borderRadius: 120 }}
56
+ />
57
+ ```
58
+
59
+ Files are cached in the app's cache directory, so later launches work offline. A ref gives you
60
+ `avatar` (bones, `setExpression`), `render()` and `snapshot()` (PNG file URI).
61
+
62
+ ### 3. React / web
63
+
64
+ ```ts
65
+ import { HumanAvatar, loadHumanPackage } from 'humanforge/human';
66
+ import { exportHumanGLB } from 'humanforge/human/export'; // optional, web only
67
+
68
+ const pkg = await loadHumanPackage('/human');
69
+ const avatar = new HumanAvatar(pkg);
70
+ scene.add(avatar.group);
71
+ await avatar.setConfig({ gender: 0.9, muscle: 0.7, hair: 'short02', eyes: 'green' });
72
+ avatar.setExpression('jawOpen', 0.4);
73
+ avatar.bones.find((b) => b.name.endsWith('LeftArm'))!.rotateZ(-0.8);
74
+ const glb = await exportHumanGLB(avatar); // Mixamo-rigged, ARKit blendshapes
75
+ ```
76
+
77
+ `demo/human.html` is a complete web editor (`npm run demo`, then open `/human.html`).
78
+
79
+ ### 4. Selfie to avatar
80
+
81
+ `fitSelfie` turns a front-facing photo into a `HumanConfig` patch. It sets the ethnicity mix, about
82
+ 45 face detail sliders, the skin texture and `skinTint`, and the eye color. It runs on-device and
83
+ has two parts:
84
+
85
+ 1. **Landmarks:** [MediaPipe Face Landmarker](https://ai.google.dev/edge/mediapipe/solutions/vision/face_landmarker)
86
+ (Apache-2.0) gives 478 3D landmarks, including the irises.
87
+ 2. **Fit** (`humanforge/human`, plain TypeScript, no WASM): each landmark is tied to a MakeHuman
88
+ vertex. The fit removes pose and scale (similarity Procrustes), then solves a box-constrained
89
+ ridge least-squares problem over the detail morphs. Macros are linearized with finite
90
+ differences. Expression morphs are fitted too and then thrown away, so a smile doesn't turn
91
+ into a wide mouth. Skin is matched from cheek and forehead pixels, and eye color from iris
92
+ pixels. The web helper detects the face, then re-detects on a full-resolution face crop, so the
93
+ irises and eye whites get enough pixels. Detection plus fit takes about 1 s in Chrome on a
94
+ 1920px photo.
95
+
96
+ **Web**
97
+
98
+ ```bash
99
+ npm i @mediapipe/tasks-vision
100
+ ```
101
+
102
+ ```ts
103
+ import { createSelfieFitter } from 'humanforge/human/selfie';
104
+
105
+ const fitter = await createSelfieFitter(); // loads the WASM + model from public CDNs
106
+ const { config, error } = await fitter.fit(pkg, file, { hints: { gender: 1 } });
107
+ await avatar.setConfig(config);
108
+ ```
109
+
110
+ `demo/human.html` has a **📷 From selfie** button. You can also open
111
+ `/human.html?gender=0&selfie=<image url>`. Self-host the WASM and model by passing
112
+ `createSelfieFitter({ wasmPath, modelPath })`.
113
+
114
+ **React Native.** There's no WASM on Hermes, so the landmark model has to run some other way. The
115
+ fit itself is plain JS and runs unchanged. Pick one of these:
116
+
117
+ - **On device:** run MediaPipe's `face_landmarker.task` (a zip of TFLite models; use the
118
+ detector and the landmark model with attention/iris refinement) through
119
+ [`react-native-fast-tflite`](https://github.com/mrousavy/react-native-fast-tflite). Decode the
120
+ outputs to 478 normalized landmarks, then call
121
+ `fitSelfie(pkg, landmarks, { width, height }, { width, height, data: rgba })`. You can get RGBA
122
+ pixels with `expo-image-manipulator` or a Skia snapshot. Without pixels, the fit still sets the
123
+ shape but not the colors.
124
+ - **Native SDKs:** MediaPipe Tasks for iOS and Android expose the same Face Landmarker. Wrap it in a
125
+ small Expo module and pass the landmarks to `fitSelfie`.
126
+ - **Server-side:** upload the photo, run MediaPipe (Python or Node + tasks-vision) next to
127
+ `fitSelfie` on the server, and return the `HumanConfig` JSON. The app stays light, but the photo
128
+ leaves the device.
129
+
130
+ **Limits.** Landmarks capture face proportions well. They don't capture fine features (lip shape
131
+ detail, nose profile, wrinkles), so expect "the same kind of face", not a likeness. Gender isn't
132
+ estimated by default: in tests on real portraits the landmarks got it right only about half the
133
+ time. Ask the user and pass `hints: { gender }`, or opt in with
134
+ `macros: ['gender', ...DEFAULT_FIT_MACROS]`. Age isn't estimated either, so pass `hints: { age }`
135
+ if you know it. Skin choice depends on the photo's lighting: dark or flash-lit photos can land on a
136
+ neighbouring tone. The result is a starting point that users can adjust with the sliders. Hair,
137
+ hair color and beards aren't fitted yet.
138
+
139
+ **Rebuilding the landmark map.** `src/human/faceMap.ts` is generated from MediaPipe's canonical face
140
+ mesh (Apache-2.0) and the MakeHuman base mesh:
141
+
142
+ ```bash
143
+ curl -LO https://raw.githubusercontent.com/google-ai-edge/mediapipe/master/mediapipe/modules/face_geometry/data/canonical_face_model.obj
144
+ npm run build:facemap -- canonical_face_model.obj --refine
145
+ # optional: open /calibrate.html in the demo, save its JSON as mean.json, then
146
+ npm run build:facemap -- canonical_face_model.obj --refine --calibration mean.json
147
+ ```
148
+
149
+ The calibration averages MediaPipe's output over the public-domain NASA portraits listed in
150
+ `tools/calibration-portraits.json`. The images are fetched at calibration time and aren't stored
151
+ in the repo. This average replaces the canonical face as the fitting reference, because the
152
+ canonical face isn't the average of MediaPipe's predictions on real faces. Without it, every
153
+ avatar picks up the same bias, such as a wider mouth.
154
+
155
+ ### Config
156
+
157
+ `HumanConfig` is plain JSON (store it as-is):
158
+
159
+ - **Macro (0..1):** `gender`, `age` (18–90), `muscle`, `weight`, `height`, `proportions`, `cupSize`,
160
+ `firmness`, and an ethnicity mix `african` / `asian` / `caucasian`.
161
+ - **Detail:** `detail: { [id]: -1..1 }`, with the ids listed in `DETAIL_SLIDERS` (head shape, jaw,
162
+ chin, cheeks, eyes, brows, nose ×9, mouth ×8, ears, neck, shoulders, chest, waist, hips…).
163
+ - **Style:** `skin`, `skinTint`, `eyes`, `eyebrows`, `eyebrowColor`, `eyelashes`, `hair`,
164
+ `hairColor`, and `clothes: string[]`. The available ids are listed in `pkg.manifest`.
165
+
166
+ ### How it works
167
+
168
+ - **Shape:** the MakeHuman macro model (gender × age × muscle × weight × height × proportions ×
169
+ breast × ethnicity) is baked into 32 gender-split basis morphs. Together with the detail targets,
170
+ it is evaluated on the CPU only when the config changes (a few ms). Expressions stay on the GPU
171
+ as morph targets, so animation is cheap.
172
+ - **Fitting:** hair, clothes, eyes, brows and teeth use MakeHuman's proxy fitting (barycentric refs
173
+ + scaled offsets), so they follow every slider. Clothes hide the body faces underneath them, so
174
+ nothing pokes through.
175
+ - **Rig:** bone heads and tails are re-derived from MakeHuman's joint helpers after each reshape,
176
+ so the skeleton always matches the body. Mixamo animations retarget directly.
177
+ - **Size:** shape data is delta-coded and byte-planed before gzip, with 8-bit values where the
178
+ error stays ≤ 0.4 mm. Geometry uses `KHR_mesh_quantization` (no decoder needed), and textures
179
+ are WebP. There's no WASM, so it works on Hermes.
180
+
181
+ ## Stylized low-poly mode
182
+
183
+ This mode has zero asset files: every part is generated from three.js primitives. An avatar
184
+ serializes to a ~60-character code.
185
+
186
+ ```tsx
187
+ import { AvatarCreator, AvatarView, decodeAvatar } from 'humanforge';
188
+
189
+ <AvatarCreator onSave={(config, code, view) => api.saveAvatar(code)} />
190
+ <AvatarView config={decodeAvatar(code)} framing="bust" style={{ width: 96, height: 96 }} />
191
+ ```
192
+
193
+ The core works without React (`buildAvatar`, `avatarToGLB` from `humanforge/core`). The editor
194
+ UI is generated from `EDITOR_SCHEMA`. When adding config fields, only append new ones so stored
195
+ codes stay valid.
196
+
197
+ ## Dev
198
+
199
+ ```bash
200
+ npm install
201
+ npm run build:human -- <mpfb data> <system assets> # see above
202
+ npm test # both modes: every option builds, codec round-trips, human package decodes, selfie solver
203
+ npm run typecheck
204
+ npm run demo # http://localhost:5173 (stylized) and /human.html (realistic)
205
+ ```
206
+
207
+ ## Status and limits
208
+
209
+ - **Verified:** the asset build, the decoder and smoke tests, and the web runtime (shape sliders,
210
+ skins, hair, clothes, rig posing, expressions, GLB export), visually in Chrome.
211
+ - **Not yet run on a device:** the React Native components (`HumanView`, `AvatarView`,
212
+ `AvatarCreator`). They typecheck against RN 0.81, expo-gl 16 and expo-file-system 19, and use
213
+ the standard expo-gl texture path (`{ localUri }`). Test on one iOS and one Android device before
214
+ shipping.
215
+ - **Selfie fit:** verified in Chrome with public-domain portraits (web path). The React Native
216
+ landmark path is documented but not implemented. See "Selfie to avatar" for its limits.
217
+ - **Wardrobe:** hairstyles and clothes are limited to the CC0 MakeHuman system packs. More CC0
218
+ packs, or your own `.mhclo` assets, can be added through `tools/build-human.ts`. There's no hijab
219
+ or kufi in the realistic set yet.
220
+ - **Hair quality:** hair uses alpha-tested cards, so the edges are crisper with MSAA (`antialias: true`).
221
+
222
+ ## Licenses
223
+
224
+ - Code: MIT.
225
+ - Human assets (base mesh, targets, skins, hair, clothes, rig, weights, expression units): CC0 1.0,
226
+ from the [MakeHuman](http://www.makehumancommunity.org) project and
227
+ [MPFB2](https://github.com/makehumancommunity/mpfb2). Credit is appreciated but not required.
228
+ - Selfie fitting: MediaPipe Face Landmarker and its canonical face mesh are Apache-2.0 (Google).
229
+ `src/human/faceMap.ts` contains vertex indices and averaged landmark positions derived from them.
230
+ The model and WASM are downloaded at runtime and aren't bundled.
231
+ - No code, meshes or textures from Avatar SDK / MetaPerson or any other proprietary product.
package/package.json CHANGED
@@ -1,6 +1,110 @@
1
1
  {
2
2
  "name": "humanforge",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Open-source avatar SDK for React and React Native: realistic MakeHuman-based humans (CC0) plus a tiny procedural low-poly mode.",
5
+ "license": "MIT",
6
+ "author": "abdulrehman043",
7
+ "homepage": "https://github.com/abdulrehman043/humanforge#readme",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/abdulrehman043/humanforge.git"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/abdulrehman043/humanforge/issues"
14
+ },
15
+ "keywords": [
16
+ "avatar",
17
+ "3d",
18
+ "react",
19
+ "react-native",
20
+ "expo",
21
+ "threejs",
22
+ "makehuman",
23
+ "selfie-to-avatar",
24
+ "blendshapes",
25
+ "arkit",
26
+ "mixamo",
27
+ "glb",
28
+ "metaperson-alternative"
29
+ ],
30
+ "main": "src/index.ts",
31
+ "react-native": "src/index.ts",
32
+ "types": "src/index.ts",
33
+ "exports": {
34
+ ".": "./src/index.ts",
35
+ "./core": "./src/core/index.ts",
36
+ "./human": "./src/human/index.ts",
37
+ "./human/export": "./src/human/export.ts",
38
+ "./human/selfie": "./src/human/selfie.ts"
39
+ },
40
+ "files": [
41
+ "src",
42
+ "README.md",
43
+ "LICENSE"
44
+ ],
45
+ "sideEffects": false,
46
+ "scripts": {
47
+ "demo": "vite",
48
+ "build:human": "tsx tools/build-human.ts",
49
+ "typecheck": "tsc --noEmit",
50
+ "test": "tsx scripts/smoke.ts && tsx scripts/human-smoke.mts && tsx scripts/fit-test.mts",
51
+ "build:facemap": "tsx tools/build-facemap.mts"
52
+ },
53
+ "peerDependencies": {
54
+ "@gltf-transform/core": ">=4",
55
+ "@gltf-transform/extensions": ">=4",
56
+ "@gltf-transform/functions": ">=4",
57
+ "expo-file-system": ">=18",
58
+ "expo-gl": ">=13",
59
+ "react": ">=18",
60
+ "react-native": ">=0.72",
61
+ "three": ">=0.160",
62
+ "@mediapipe/tasks-vision": ">=0.10"
63
+ },
64
+ "peerDependenciesMeta": {
65
+ "expo-gl": {
66
+ "optional": true
67
+ },
68
+ "react-native": {
69
+ "optional": true
70
+ },
71
+ "expo-file-system": {
72
+ "optional": true
73
+ },
74
+ "@gltf-transform/core": {
75
+ "optional": true
76
+ },
77
+ "@gltf-transform/extensions": {
78
+ "optional": true
79
+ },
80
+ "@gltf-transform/functions": {
81
+ "optional": true
82
+ },
83
+ "@mediapipe/tasks-vision": {
84
+ "optional": true
85
+ }
86
+ },
87
+ "devDependencies": {
88
+ "@gltf-transform/core": "^4.5.1",
89
+ "@gltf-transform/extensions": "^4.5.1",
90
+ "@gltf-transform/functions": "^4.5.1",
91
+ "@mediapipe/tasks-vision": "^1.1.1",
92
+ "@types/react": "^18.3.12",
93
+ "@types/three": "^0.186.0",
94
+ "expo-file-system": "^19.0.24",
95
+ "expo-gl": "^16.0.10",
96
+ "expo-modules-core": "^3.0.30",
97
+ "gl-matrix": "^3.4.4",
98
+ "meshoptimizer": "^1.3.0",
99
+ "react": "^18.3.1",
100
+ "react-native": "^0.81.6",
101
+ "sharp": "^0.35.5",
102
+ "three": "^0.186.1",
103
+ "tsx": "^4.23.15",
104
+ "typescript": "^5.6.3",
105
+ "vite": "^8.3.4"
106
+ },
107
+ "dependencies": {
108
+ "fflate": "^0.8.3"
109
+ }
110
+ }