Instructions to use townbox/lowpoly-coder-4b with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use townbox/lowpoly-coder-4b with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("image-text-to-text", model="townbox/lowpoly-coder-4b") messages = [ { "role": "user", "content": [ {"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/p-blog/candy.JPG"}, {"type": "text", "text": "What animal is on the candy?"} ] }, ] pipe(text=messages)# pip install -U transformers accelerate # Load model directly from transformers import AutoProcessor, AutoModelForMultimodalLM processor = AutoProcessor.from_pretrained("townbox/lowpoly-coder-4b") model = AutoModelForMultimodalLM.from_pretrained("townbox/lowpoly-coder-4b", device_map="auto") messages = [ { "role": "user", "content": [ {"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/p-blog/candy.JPG"}, {"type": "text", "text": "What animal is on the candy?"} ] }, ] inputs = processor.apply_chat_template( messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt", ).to(model.device) outputs = model.generate(**inputs, max_new_tokens=256) print(processor.decode(outputs[0][inputs["input_ids"].shape[-1]:])) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use townbox/lowpoly-coder-4b with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "townbox/lowpoly-coder-4b" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "townbox/lowpoly-coder-4b", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker
docker model run hf.co/townbox/lowpoly-coder-4b
- SGLang
How to use townbox/lowpoly-coder-4b with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "townbox/lowpoly-coder-4b" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "townbox/lowpoly-coder-4b", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "townbox/lowpoly-coder-4b" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "townbox/lowpoly-coder-4b", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }' - Docker Model Runner
How to use townbox/lowpoly-coder-4b with Docker Model Runner:
docker model run hf.co/townbox/lowpoly-coder-4b
LowPoly Coder 4B
LowPoly Coder 4B turns a one-sentence request, such as "Make a low-poly okapi.", into JavaScript for Three.js r180 that builds that object as a small low-poly 3D asset. The script export_glb.mjs in this repo turns the reply into a .glb file.
It is a fine-tune of Qwen3.5-4B, and it is often wrong. In a blinded test on objects it had never seen, about 1 in 4 outputs was recognisable as the requested object (24.0%). DeepSeek V4.1 Flash, which wrote the model's first-stage training examples, scored 93.7% on the same test. Sample several attempts per request and keep the best one.
| Input | A one-sentence text request, e.g. "Build a low-poly washing machine." |
| Output | A 4-line spec, then one JavaScript block defining buildAsset(THREE) |
| Asset | A THREE.Group of named, flat-shaded meshes, with colours only (no textures) |
| Size | The model picks its own triangle budget: 250 to 1,100 on the test set, median 550 |
| Quality | 24% of outputs recognisable; under 1% rated "good" |
| Weights | Qwen3.5-4B with LoRA adapters merged in; bf16, 2 safetensors shards, about 9.3 GB |
| Licence | Apache-2.0 |
What the model writes
A reply has two parts:
- A short spec, which the model fills in itself:
Asset: … Triangle budget: … Brief: … Required named parts: … - One
javascriptcode block that definesfunction buildAsset(THREE). It returns aTHREE.Groupof named meshes, each with one flat-shadedMeshStandardMaterial.
- Conventions: Y is up, the object rests on y = 0, the front faces +Z, and scale is free.
- Style: always the same hand-crafted "woodland collection" look, with a fixed palette: oak, darkWood, ivory, iron, brass, sage, navy, black and rust.
The full output contract is in system_prompt.md.
How to use
1. Generate replies
- System prompt: send the exact contents of
system_prompt.mdas the system message. The model was trained with it. - User message: just the request.
- Thinking: off (
enable_thinking=Falsein the chat template). - Sampling: temperature 0.7, top_p 0.8, top_k 20, max_tokens 8192.
This is the tested setup (vLLM 0.33.0). Loading the model by repo id this way reproduced the evaluation outputs exactly.
from huggingface_hub import hf_hub_download
from vllm import LLM, SamplingParams
REPO = "townbox/lowpoly-coder-4b"
llm = LLM(model=REPO, max_model_len=12288, limit_mm_per_prompt={"image": 0, "video": 0})
system = open(hf_hub_download(REPO, "system_prompt.md")).read()
msgs = [{"role": "system", "content": system}, {"role": "user", "content": "Build a low-poly washing machine."}]
prompt = llm.get_tokenizer().apply_chat_template(msgs, tokenize=False, add_generation_prompt=True, enable_thinking=False)
out = llm.generate([prompt], SamplingParams(temperature=0.7, top_p=0.8, top_k=20, max_tokens=8192, n=4))
for i, o in enumerate(out[0].outputs):
open(f"reply_{i}.txt", "w").write(o.text)
The model keeps Qwen3.5-4B's architecture (Qwen3_5ForConditionalGeneration), but it is used text-only, so the tested setup switches image and video inputs off.
2. Turn a reply into a .glb file
npm install three@0.180.0
node export_glb.mjs reply_0.txt asset.glb
export_glb.mjs extracts the code block, runs buildAsset(THREE) in a node:vm context with a 10-second limit, and writes a GLB with one node per named mesh, flat-shaded, with base colours. In testing, all 12 real replies converted. The files passed the Khronos glTF validator with 0 errors and 0 warnings, and their mesh and triangle counts matched the evaluation runtime exactly.
Safety: the generated code is untrusted, and
node:vmis not a security boundary. Run the export in a container or VM with no network access and no secrets.
3. Sample several and keep the best
A single attempt is often weak or broken. Sample several, for example 4 to 8 (the snippet above draws 4), and keep the best one. The snippet's own request, "Build a low-poly washing machine.", was also in the test set. One of its outputs is in the typical outputs below, with a mean score of 1.0 of 3.
Earlier tests of closely related models found that picking the best of 8 with a vision model (GPT-6.1 Terra scoring rendered views) raised the recognisable rate by about 23 to 28 points. This was not measured for this exact model.
Results
How it was tested
Requests: 90 requests for objects whose names appear nowhere in the training data, in 10 categories. The model made 2 attempts at each, giving 180 outputs.
Renders: every output was rendered from 6 views: front, back, left, right, front-left and back-right.
Raters: 5 independent blinded raters, all AI models (3 Claude Opus 5.5 instances, Gemini 3.8 Flash and Claude Sonnet 5.5), scored every output from 0 to 3:
- 0: wrong, broken or empty;
- 1: weak, the object can be guessed;
- 2: clearly the requested object, an acceptable asset;
- 3: good.
Agreement between raters was Krippendorff's α = 0.81. A score of 2 or more counts as recognisable.
Main result
| LowPoly Coder 4B | DeepSeek V4.1 Flash (wrote the first-stage training examples) | |
|---|---|---|
| Recognisable (score ≥ 2) | 24.0% (95% CI 18.0–30.2%) | 93.7% |
Score 3 ("good") is under 1% of outputs.
Technical checks
| Check | Outputs (of 180) |
|---|---|
| Ran and rendered | 172 |
| Passed every check (closed parts, within its budget, all parts connected) | 116 |
| Had detached, floating parts | 51 |
By category
| Category | Recognisable |
|---|---|
| birds, sea creatures and bugs | 56% |
| animals | 47% |
| containers and food | 29% |
| props | 20% |
| architecture | 17% |
| fantasy and game | 17% |
| vehicles | 14% |
| furniture | 14% |
| plants | 14% |
| tools and gear | 6% |
Each category has 10 to 20 outputs, so these rates are noisy.
What the outputs look like
These are real test-set outputs, shown from the front-left view and cropped from the evaluation renders. Each label gives the request, the mean of the 5 raters' scores (0 to 3), how many of the 5 raters scored it 2 or more, and the result of the technical checks.
Best outputs
These are among the highest-scoring outputs. They include a brown bungalow with a gable roof, chimney, door and windows (which still has detached parts), a yellow omelette, an orange-and-black oriole, a yellow canary, a round-eared brown quokka and a red flower in a pot (the request asked for a vase). Even the best outputs are simple shapes in flat colours.
Typical outputs
About 3 in 4 outputs fall below the recognisable bar, like these. They are often the right kind of thing but not clearly the requested object:
- the washing machine is a beige cabinet with a knob and a curved line, but no round door;
- the okapi is a plain brown, deer-like animal with no stripes, and has detached parts;
- the cement mixer's drum lies sideways across the truck;
- the typewriter is a dark block with a row of keys and a paper bar on top;
- the ramen is a bowl of red broth with a few sticks in it;
- the dugong is a thin, dark, torpedo-like shape.
Failures
These are among the lowest-scoring outputs that rendered:
- the radiator, playpen, troika sleigh and stockade are loose pieces floating in the air;
- the teepee is an upside-down pyramid;
- "steak on a plate" is an empty plate.
The teepee and the plate passed every technical check, so passing the checks does not mean the object is right. Outputs that did not run or render at all are not shown; 172 of the 180 outputs ran and rendered.
Limitations
- Low success rate: about 3 in 4 outputs are not recognisable, and under 1% are rated "good". Plan to sample several attempts per request and pick the best by eye or with a vision model.
- Broken geometry is common: only 116 of 180 test outputs passed every technical check, and 51 had detached, floating parts.
- Uneven across categories: birds, sea creatures and bugs (56%) and animals (47%) do best. Tools and gear (6%) and furniture, plants and vehicles (14% each) do worst.
- Not a scene generator: when asked for scenes with hundreds of objects (aquarium, reef, pond, forest, village, city block; 24 attempts), the model builds one small asset of about 1,000 triangles with roughly 11 to 25 objects. A small village block worked best, and the aquarium tanks came out as opaque boxes.
- One look: every output uses the same flat-shaded "woodland collection" style and fixed palette, with no textures.
- Untrusted code: the model writes code that you then execute. Always run it in an isolated container or VM, as described above.
Training
The model was fine-tuned on request → spec + code examples in two stages: 7,599 written by DeepSeek V4.1 Flash, then 4,245 of its own outputs that a vision model (GPT-6.1 Terra) rated as recognisable, all kept only if they passed the technical checks. Both stages are published, with the held-out validation rows, in townbox/lowpoly-coder-4b-data.
Files
| File | What it is |
|---|---|
*.safetensors (2 shards) |
Merged bf16 weights, about 9.3 GB |
system_prompt.md |
The exact system prompt the model was trained with; always send it |
export_glb.mjs |
Converts a model reply into a .glb file |
images/ |
The render images on this card |
Licence
Apache-2.0, inherited from Qwen3.5-4B. The training data contains code written by deepseek/deepseek-v4.1-flash-1017, so that model's terms may also apply to the data.
- Downloads last month
- 21



