img2threejs / README.md
Mike0021's picture
Increase reasoning-spec generation headroom
f074dc6 verified
|
Raw
History Blame Contribute Delete
9.6 kB
---
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.