File size: 9,600 Bytes
62fdf85
39ff632
 
 
 
62fdf85
39ff632
62fdf85
39ff632
37e3d5a
39ff632
 
 
 
 
 
 
62fdf85
 
39ff632
 
 
 
 
 
 
37e3d5a
 
 
39ff632
 
37e3d5a
 
 
39ff632
 
 
 
 
37e3d5a
39ff632
 
 
 
37e3d5a
39ff632
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37e3d5a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39ff632
 
 
 
 
 
 
 
 
 
 
f074dc6
39ff632
 
 
 
37e3d5a
 
 
 
f074dc6
bf1fb5f
 
 
 
 
39ff632
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37e3d5a
 
 
39ff632
 
 
 
 
37e3d5a
 
 
39ff632
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37e3d5a
 
 
 
39ff632
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37e3d5a
39ff632
37e3d5a
39ff632
 
 
 
37e3d5a
 
 
 
 
 
39ff632
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
---
title: img2threejs
emoji: 🧊
colorFrom: indigo
colorTo: gray
sdk: docker
app_port: 7860
pinned: false
license: mit
short_description: Image to procedural Three.js with community gallery
tags:
  - threejs
  - image-to-3d
  - procedural-generation
  - code-generation
  - llm
startup_duration_timeout: 30m
---

# img2threejs — image to procedural Three.js

Upload one object reference image. A vision-capable LLM authors an
`ObjectSculptSpec`; deterministic validators enforce the upstream structural
and strict-quality rules; and the vendored generator emits a TypeScript
`THREE.Group` factory. The result renders in a sandboxed browser viewer and is
downloadable as the original spec, TypeScript, an ESM bundle, and standalone
HTML. Successful results are shared to the public community gallery by
default; the clearly labelled upload-page toggle lets each user keep a result
out of the gallery before generation begins.

This Docker Space adapts [hoainho/img2threejs](https://github.com/hoainho/img2threejs)
(MIT), synced to v1.3.0 commit
`2c78038d315ec93086ef308c17ad2f1cd4cca683`. It is reconstruction-by-code,
not photogrammetry, mesh extraction, or a downloaded asset pack.

## What the hosted workflow guarantees

```text
image ──▶ byte/type/decompression guard + deterministic probe
      ──▶ vision LLM authors schema-2.1 ObjectSculptSpec
      ──▶ strict validator (errors return to the LLM, up to 3 repair rounds)
      ──▶ original spec saved locked and unreviewed
      ──▶ separate hosted-preview compile manifest (all supported parts)
      ──▶ vendored generator + esbuild ──▶ sandboxed viewer/downloads
      ──▶ optional persistent community publication (default on)
```

The hosted preview does **not** fabricate upstream review evidence. The
downloaded `spec.json` retains an empty `reviewHistory` and its original locked
pass order. A separate compile copy marks itself
`hosted-unreviewed-preview` and combines the declared components into one
preview pass. It contains no screenshot comparison, AI-vision score, reviewer,
or `continue` decision.

The result is an approximate, stylized procedural reconstruction from a single
image. Hidden sides are inferred rather than observed or measured. Structural
strict validation is not visual approval; production approval requires running
the upstream pass-by-pass render, comparison, and review loop outside this
hosted preview. Unsupported geometry families and parent cycles are rejected
instead of being replaced by placeholder boxes.

## Community gallery and privacy

The upload page starts with **Share this result to the community gallery**
enabled. The disclosure beside it states that publication includes the
uploaded reference image and generated artifacts. Turn it off before choosing
Generate to keep the job out of the gallery; opted-out and failed jobs are
never copied to gallery storage. Opted-out job URLs are unlisted rather than
access-controlled and expire with temporary job storage.

Published entries are written atomically to a private Hugging Face Bucket
mounted at `/data` and are exposed only through the gallery API below. They
survive app restarts, unlike the short-lived per-job files under `/tmp`.
Gallery cards open the real generated bundle in the same sandboxed viewer and
offer the same downloads. The viewer provides orbit, zoom, pan, wireframe,
shadow, camera-reset, and screenshot controls.

## Required Space Secrets

Conversion needs a vision-capable Anthropic Messages or OpenAI-compatible chat
endpoint. Add these under **Settings → Secrets**:

| Secret | Required | Example | Purpose |
| --- | --- | --- | --- |
| `LLM_API_KEY` | yes | provider API key | Credential; never logged or returned to the browser. |
| `LLM_MODEL` | yes | `claude-sonnet-4-5`, `moonshotai/kimi-k3` | Provider model identifier; it must accept images. |
| `LLM_BASE_URL` | no | `https://api.anthropic.com` | Provider base URL; defaults to Anthropic. |
| `LLM_API_STYLE` | no | `auto`, `anthropic`, or `openai` | `auto` tries Messages then falls back on HTTP 404. |
| `LLM_MAX_TOKENS` | no | `32768` | Response budget for reasoning plus the structured spec. |
| `LLM_TIMEOUT_S` | no | `180` | Per-request timeout in seconds. |
| `LLM_MAX_RETRIES` | no | `2` | Transient provider retries. |
| `LLM_REFERER`, `LLM_TITLE` | no | provider-specific | Optional attribution headers. |

Set the public Space variable `GALLERY_DIR=/data/gallery`. The production
Space mounts `hf://buckets/Mike0021/img2threejs-gallery` at `/data`; another
deployment should mount its own Bucket at the same path.

Optional operational variables are `JOB_TIMEOUT_S` (default `1800`, covering
queue time through the completed browser bundle) and
`GALLERY_PUBLISH_TIMEOUT_S` (default `120`, bounding the isolated persistent
Bucket copy). Long opaque stages report elapsed time every 25 seconds; the UI
does not invent completion percentages.

`ANTHROPIC_API_KEY`/`ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_BASE_URL`, and
`ANTHROPIC_MODEL` are accepted as server-side aliases. Without a key and model,
the app still starts, `/health` reports `llm_configured: false`, and job creation
returns `503 llm_not_configured`; no model or placeholder is emitted.

## HTTP API

```bash
curl https://<space-host>/health
curl -F "file=@object.png" https://<space-host>/api/jobs
curl -N https://<space-host>/api/jobs/<job-id>/events
curl https://<space-host>/api/jobs/<job-id>/artifacts/spec.json
curl https://<space-host>/api/jobs/<job-id>/artifacts/factory.ts
curl https://<space-host>/api/jobs/<job-id>/artifacts/model.bundle.js
curl https://<space-host>/api/jobs/<job-id>/artifacts/standalone.html
curl 'https://<space-host>/api/gallery?offset=0&limit=24'
curl https://<space-host>/api/gallery/<gallery-id>
curl https://<space-host>/api/gallery/<gallery-id>/artifacts/standalone.html
```

Uploads are limited to PNG, JPEG, WebP, GIF, or BMP, 10 MiB, and 40 MP by
default. SVG is rejected. Images are orientation-normalized, metadata-stripped,
and downscaled to at most 1024 px before processing. Defaults permit 10 jobs per
hour per client, 2 active conversions, and 8 queued/running jobs total. In the
multipart job request, `share` defaults to `true`; send `share=false` to opt
out.

## Run and test locally

Python 3.12 and Node 22 are the tested versions.

```bash
python3 -m pip install -r requirements.txt
npm ci
pytest -q
python3 forge/tests/test_pipeline.py

LLM_API_KEY=... LLM_MODEL=... LLM_BASE_URL=... python3 -m app.main
# http://127.0.0.1:7860
```

Build and verify the same target architecture used by Hugging Face:

```bash
docker build --platform=linux/amd64 -t img2threejs .
docker run --rm -p 7860:7860 \
  -e LLM_API_KEY -e LLM_MODEL -e LLM_BASE_URL img2threejs

scripts/verify_docker.sh
```

The Docker build regenerates a fixture factory through the real strict gate
and hosted-preview compiler, bundles it with esbuild, and executes the bundle
headlessly in Node. The final runtime image contains only runtime dependencies,
`app/`, the required `forge/` source, and the license—not tests, scripts,
rollouts, virtual environments, caches, or the upstream comparison clone.

## Deploy and verify

The helper uses the currently authenticated local `hf` CLI account; an
`HF_TOKEN` environment variable is not required. `--dry-run` performs no Hub
mutation and prints the exact deterministic upload allowlist.

```bash
hf auth whoami
hf buckets create Mike0021/img2threejs-gallery --private --exist-ok
hf spaces volumes set Mike0021/img2threejs \
  --volume hf://buckets/Mike0021/img2threejs-gallery:/data
hf spaces variables add Mike0021/img2threejs --env GALLERY_DIR=/data/gallery
python3 scripts/deploy_space.py --dry-run
python3 scripts/deploy_space.py                 # sets present LLM_* aliases as Secrets
# or: python3 scripts/deploy_space.py --no-secrets

hf spaces logs Mike0021/img2threejs --build --follow
hf spaces logs Mike0021/img2threejs --follow

python3 scripts/verify_space.py                 # wait/info/logs + GET-only probes
python3 scripts/verify_space.py \
  --e2e tests/fixtures/mug_photo.png            # real SSE job + artifact/node smoke
```

The deploy helper stages only its allowlist and issues one sanitized
`hf upload`. Secret values are passed in a temporary mode-0600 secrets file;
only secret names are printed. The live verifier never submits a job unless an
explicit `--e2e IMAGE` is supplied.

## Repository layout

| Path | Purpose |
| --- | --- |
| `app/` | FastAPI service, LLM client, pipeline adapter, SPA, and sandboxed viewer |
| `forge/` | Vendored upstream v1.3 pipeline with targeted runtime generator/safety fixes |
| `grimoire/`, `SKILL.md` | Upstream rubrics and complete agent workflow |
| `UPSTREAM_REVISION` | Exact upstream version and commit provenance |
| `tests/` | Unit, HTTP, pipeline, manifest, and render smoke tests |
| `scripts/` | Fixture builder, Node smoke, Docker verifier, deployer, live verifier |
| `docs/SECURITY.md` | Threat model, controls, and accepted limitations |

The targeted `forge/` changes preserve declared component dimensions without
scaling child pivots, avoid misinterpreting non-axis attachments, detect parent
cycles, and retain explicit hosted-preview honesty labels. The Space supports
the v1.3 advanced primitive set instead of replacing unknown geometry with
boxes. The adapter is therefore intentionally not a byte-for-byte upstream
tree.

## License and attribution

MIT — © 2026 hoainho (upstream) and this Space's contributors. See
`LICENSE`. three.js is MIT © the three.js authors.