Instructions to use hotocooo/Spark-X2.6-4B-Blender with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use hotocooo/Spark-X2.6-4B-Blender with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="hotocooo/Spark-X2.6-4B-Blender", trust_remote_code=True) messages = [ {"role": "user", "content": "Who are you?"}, ] pipe(messages)# pip install -U transformers accelerate # Load model directly from transformers import AutoModelForCausalLM model = AutoModelForCausalLM.from_pretrained("hotocooo/Spark-X2.6-4B-Blender", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use hotocooo/Spark-X2.6-4B-Blender with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "hotocooo/Spark-X2.6-4B-Blender" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "hotocooo/Spark-X2.6-4B-Blender", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/hotocooo/Spark-X2.6-4B-Blender
- SGLang
How to use hotocooo/Spark-X2.6-4B-Blender 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 "hotocooo/Spark-X2.6-4B-Blender" \ --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": "hotocooo/Spark-X2.6-4B-Blender", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'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 "hotocooo/Spark-X2.6-4B-Blender" \ --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": "hotocooo/Spark-X2.6-4B-Blender", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Docker Model Runner
How to use hotocooo/Spark-X2.6-4B-Blender with Docker Model Runner:
docker model run hf.co/hotocooo/Spark-X2.6-4B-Blender
Spark-X2.6-4B-Blender
A 4B model that writes Blender 5.x Python (bpy) scripts which build clean, quad-dominant meshes:
hard-surface props, furniture, products, and anatomically proportioned organic base meshes (humans, hands, quadrupeds).
Full-parameter fine-tune of XHToken/Spark-X2.6-4B-Base (all 4.1B weights, no adapters). Every training script was executed headless in Blender and kept only if it ran and produced geometry; topology was scored on the evaluated mesh.
Results
All numbers come from running the model's output in Blender 5.2.1 LTS headless, greedy decoding. run = script executed and built geometry · topo = mean topology score of those (max 1.0) · fid = fidelity to the reference answer (size, proportions, part count; failed scripts count as 0).
| Model | Stage | BlendNet held-out | Anatomy (unseen) | 3DCode held-out | Spatial (held-out) | BlenderWorld text | Extreme (unseen) | Hard constraints | Objaverse (held-out) | Real-world prompts |
|---|---|---|---|---|---|---|---|---|---|---|
base |
untuned XHToken/Spark-X2.6-4B-Base |
2.0% run · topo 0.955 · fid 0.004 | 0.0% run · topo 0.000 · fid 0.000 | 2.0% run · topo 1.000 · fid 0.005 | 6.7% run · topo 0.750 · fid 0.049 · QA 0/100 | 0.0% run · topo 0.000 | – | – | – | – |
v1 |
full fine-tune (SFT) on verified BlendNet + procedural anatomy | 99.0% run · topo 0.877 · fid 0.811 | 100.0% run · topo 0.994 · fid 0.959 | 42.0% run · topo 0.943 · fid 0.161 | 95.0% run · topo 1.000 · fid 0.803 · QA 0/100 | – | – | – | – | – |
v3 |
v1 + rejection-sampling round (best-of-N winners plus agent repair / refine / polish turns) | 100.0% run · topo 0.943 · fid 0.801 | 100.0% run · topo 0.994 · fid 0.963 | 40.0% run · topo 0.976 · fid 0.162 | 100.0% run · topo 1.000 · fid 0.979 · QA 35/100 | 66.7% run · topo 0.940 | exact 0.0% · score 0.043 · free 0.0% | constraint 0.0% · score 0.077 | norm 0.238 · beats box 49/78 · judge top-20 10% (median rank 206) | 100.0% run · topo 0.958 |
v4 |
v3 + short submit warm-up SFT + agentic GRPO-family RL (multi-turn episodes in Blender, DAPO loss, verifier reward) | – | – | – | – | – | – | – | – | – |
v5 |
v3 + SFT on Blender-verified procedural generator data (lathe/panel: curved, bevelled, materials) + anatomy/spatial/BlendNet replay | 99.0% run · topo 0.936 · fid 0.792 | 100.0% run · topo 0.995 · fid 0.974 | 30.0% run · topo 0.975 · fid 0.128 | 100.0% run · topo 1.000 · fid 0.997 · QA 38/100 | 53.8% run · topo 0.963 | exact 0.0% · score 0.093 · free 0.0% | constraint 0.0% · score 0.071 | – · judge top-20 12% (median rank 263) | 87.5% run · topo 0.955 |
v6 |
v5 + full SFT on part-composition data (numbers-as-variables, unseen kits) + replay of generator/anatomy/agent/BlendNet/spatial/general rows | 99.0% run · topo 0.946 · fid 0.785 | 100.0% run · topo 0.995 · fid 0.983 | 32.0% run · topo 0.962 · fid 0.164 | 100.0% run · topo 1.000 · fid 0.998 · QA 41/100 | 46.2% run · topo 0.939 | exact 0.0% · score 0.205 · free 0.0% | constraint 0.0% · score 0.074 | – · judge top-20 13% (median rank 351) | – |
v7 |
full SFT from base on one deduplicated pool (BlendNet whole, generator/fit/anatomy capped per template) | 100.0% run · topo 0.893 · fid 0.797 | 100.0% run · topo 0.997 · fid 0.951 | 36.0% run · topo 0.949 · fid 0.146 | 100.0% run · topo 1.000 · fid 0.997 · QA 40/100 | 30.8% run · topo 0.956 | exact 0.0% · score 0.091 · free 0.0% | constraint 0.0% · score 0.069 | – · judge top-20 6% (median rank 492) | 87.5% run · topo 0.978 |
v3d |
v3 + DPO on its own best-of-N samples (best finished answer vs loop/failure) | 100.0% run · topo 0.942 · fid 0.802 | 100.0% run · topo 0.994 · fid 0.963 | 42.0% run · topo 0.971 · fid 0.169 | 100.0% run · topo 1.000 · fid 0.979 · QA 35/100 | 76.9% run · topo 0.946 | exact 0.0% · score 0.046 · free 0.0% | constraint 0.0% · score 0.081 | norm 0.235 · beats box 51/78 · judge top-20 12% (median rank 208) | 100.0% run · topo 0.960 |
v3dr |
v3d + DPO on open-world samples graded against real meshes (Objaverse-LVIS training categories) | 98.0% run · topo 0.961 · fid 0.788 | 100.0% run · topo 0.997 · fid 0.964 | 42.0% run · topo 0.918 · fid 0.173 | 100.0% run · topo 1.000 · fid 0.981 · QA 34/100 | 79.5% run · topo 0.949 | exact 0.0% · score 0.036 · free 0.0% | constraint 0.0% · score 0.077 | norm 0.466 · beats box 58/78 · judge top-20 11% (median rank 214) | 100.0% run · topo 0.959 |
v3drr |
v3dr + DPO round 2 on fresh open-world samples (same real-mesh grading) | 99.0% run · topo 0.953 · fid 0.791 | 100.0% run · topo 0.998 · fid 0.965 | 40.0% run · topo 0.934 · fid 0.165 | 98.3% run · topo 1.000 · fid 0.964 · QA 34/100 | 79.5% run · topo 0.956 | exact 0.0% · score 0.029 · free 0.0% | constraint 0.0% · score 0.067 | norm 0.589 · beats box 58/78 · judge top-20 17% (median rank 226) | 100.0% run · topo 0.967 |
v3drrr |
v3drr + DPO round 3 with fp32 master weights (fp32 master copy + bf16 Adam moments) | 100.0% run · topo 0.962 · fid 0.797 | 100.0% run · topo 0.998 · fid 0.964 | 30.0% run · topo 0.964 · fid 0.123 | 98.3% run · topo 1.000 · fid 0.962 · QA 33/100 | 76.9% run · topo 0.949 | exact 0.0% · score 0.039 · free 0.0% | constraint 0.0% · score 0.074 | norm 0.634 · beats box 59/78 · judge top-20 16% (median rank 245) | 100.0% run · topo 0.969 |
v3w1 |
v3d + SFT on part-family kit generators for ~180 open-world object families (detailed and vague prompts) + replay | – | – | – | – | – | – | – | – | – |
Held-out BlendNet rows and anatomy eval prompts are never seen in training (anatomy prompts use a different random seed). BlenderWorld is scored for execution and topology only; its rubric judge was not run.
Every stage is also compared with the base model on identical prompts: exact McNemar test on which prompts pass, shape F-score@0.05 and Chamfer distance against the reference's surface point cloud (2048 area-weighted samples, both normalised for placement and size), with 95% bootstrap intervals. A stage counts as better only if it wins on every suite at p < 0.05 without general-text perplexity rising more than 1.05× (criteria fixed before results).
Sample outputs: best and worst
Rendered straight from this model's generated scripts on held-out prompts (greedy, one attempt), ranked by the suite's metric.
BlendNet: 100/100 scripts built (failures render nothing).
| fidelity | prompt | output | |
|---|---|---|---|
| best | 1.000 | Design a pet feeding bowl with a single compartment. | ![]() |
| best | 1.000 | Create a 3D model of a sandwich. The sandwich should have two slices of bread with a filling in between. The filling should consist of layers of ingredients … | ![]() |
| worst | 0.219 | Design a 3D model of a company logo. The logo should be simple and geometric, featuring a combination of circles and lines. | ![]() |
| worst | 0.393 | Can you design a water bottle with a cylindrical body and a screw-on cap? Make sure it includes a loop for easy carrying. Add a measurement scale on the side. | ![]() |
Anatomy: 60/60 scripts built (failures render nothing).
| fidelity | prompt | output | |
|---|---|---|---|
| best | 0.998 | Make a low-poly-to-subdivided base mesh of a athletic female human body, 1.58 m tall (7.2 heads), in A-pose with anatomically correct proportions. | ![]() |
| best | 0.994 | I need a cat body (0.39 m from shoulder to rump), standing, digitigrade legs and a tail for sculpting later. Keep the topology clean and quad-based. | ![]() |
| worst | 0.920 | I need a average male human body, 1.58 m tall (7.5 heads), in relaxed for sculpting later. Keep the topology clean and quad-based. | ![]() |
| worst | 0.920 | I need a athletic male human body, 1.90 m tall (7.5 heads), in relaxed for sculpting later. Keep the topology clean and quad-based. | ![]() |
3DCode: 20/50 scripts built (failures render nothing).
| fscore | prompt | output | |
|---|---|---|---|
| best | 1.000 | Create the 3D object of a double panel door using Python Blender code. Start by defining the main door slab using a scaled cube primitive with a height of … | ![]() |
| best | 0.996 | Create the 3D object of an arched panel door using Python Blender code. Begin by constructing the main rectangular door slab from a scaled cube primitive. Form … | ![]() |
| worst | 0.000 | Create the 3D object of a leather coral using Python Blender code. Start by generating a base polygon mesh consisting of two distinct colony centers using a … | ![]() |
| worst | 0.094 | Create the 3D object of a pair of hybrid chopsticks using Python Blender code. Construct each chopstick with two main components: a thick wooden handle and a … | ![]() |
Spatial assemblies: 60/60 scripts built (failures render nothing).
| fidelity | prompt | output | |
|---|---|---|---|
| best | 1.000 | Create a table with a 0.9612 m by 1.0932 m top 0.0428 m thick, 0.9819 m tall overall, on four square legs 0.0648 m wide, each inset 0.056 m from the top's … | ![]() |
| best | 1.000 | Model a shelf unit 0.8004 m wide, 0.2748 m deep and 1.6027 m tall: two side panels and 4 evenly spaced boards (bottom on the floor, top flush with the sides), … | ![]() |
| worst | 0.756 | Create a grid of 5 rows by 4 columns of 0.1164 m cubes with 0.1154 m gaps between them, centred on the origin and resting on the ground. Compute each part's … | ![]() |
| worst | 0.862 | Build a grid of 3 rows by 3 columns of 0.2442 m cubes with 0.337 m gaps between them, centred on the origin and resting on the ground. Place every part … | ![]() |
Language-model metrics (same tokens, run vs base)
| Run | Text | Perplexity base → run | Top-1 token acc. | KL(run‖base) per token | Loss change [95% CI] |
|---|---|---|---|---|---|
v1 |
domain (all) | 1.49 → 1.08 | 0.891 → 0.973 | 0.329 | -0.325 [-0.346, -0.307] |
v1 |
domain (clean) | 1.49 → 1.09 | 0.892 → 0.969 | 0.318 | -0.311 [-0.331, -0.292] |
v1 |
general (wikitext-2) | 9.73 → 11.43 | 0.519 → 0.499 | 0.162 | +0.161 [+0.149, +0.173] |
v3 |
domain (all) | 1.49 → 1.09 | 0.891 → 0.970 | 0.334 | -0.317 [-0.337, -0.299] |
v3 |
domain (clean) | 1.49 → 1.10 | 0.892 → 0.966 | 0.323 | -0.302 [-0.322, -0.283] |
v3 |
general (wikitext-2) | 9.73 → 10.44 | 0.519 → 0.507 | 0.093 | +0.070 [+0.063, +0.076] |
v5 |
domain (all) | 1.49 → 1.09 | 0.891 → 0.970 | 0.336 | -0.312 [-0.332, -0.295] |
v5 |
domain (clean) | 1.49 → 1.10 | 0.892 → 0.966 | 0.325 | -0.298 [-0.317, -0.278] |
v5 |
general (wikitext-2) | 9.73 → 10.53 | 0.519 → 0.505 | 0.103 | +0.079 [+0.073, +0.085] |
v6 |
domain (all) | 1.49 → 1.10 | 0.891 → 0.969 | 0.342 | -0.308 [-0.328, -0.290] |
v6 |
domain (clean) | 1.49 → 1.11 | 0.892 → 0.966 | 0.331 | -0.294 [-0.314, -0.274] |
v6 |
general (wikitext-2) | 9.73 → 10.50 | 0.519 → 0.506 | 0.103 | +0.076 [+0.069, +0.083] |
v7 |
domain (all) | 1.49 → 1.08 | 0.891 → 0.973 | 0.324 | -0.325 [-0.344, -0.307] |
v7 |
domain (clean) | 1.49 → 1.09 | 0.892 → 0.969 | 0.314 | -0.311 [-0.330, -0.292] |
v7 |
general (wikitext-2) | 9.73 → 10.25 | 0.519 → 0.511 | 0.077 | +0.052 [+0.046, +0.058] |
v3d |
domain (all) | 1.49 → 1.09 | 0.891 → 0.970 | 0.335 | -0.317 [-0.337, -0.299] |
v3d |
domain (clean) | 1.49 → 1.10 | 0.892 → 0.966 | 0.324 | -0.302 [-0.322, -0.283] |
v3d |
general (wikitext-2) | 9.73 → 10.44 | 0.519 → 0.506 | 0.093 | +0.070 [+0.063, +0.076] |
v3dr |
domain (all) | 1.49 → 1.09 | 0.891 → 0.970 | 0.340 | -0.315 [-0.335, -0.297] |
v3dr |
domain (clean) | 1.49 → 1.10 | 0.892 → 0.966 | 0.329 | -0.300 [-0.320, -0.281] |
v3dr |
general (wikitext-2) | 9.73 → 10.43 | 0.519 → 0.507 | 0.093 | +0.069 [+0.063, +0.076] |
v3drr |
domain (all) | 1.49 → 1.10 | 0.891 → 0.969 | 0.345 | -0.310 [-0.330, -0.292] |
v3drr |
domain (clean) | 1.49 → 1.11 | 0.892 → 0.965 | 0.334 | -0.295 [-0.314, -0.275] |
v3drr |
general (wikitext-2) | 9.73 → 10.43 | 0.519 → 0.507 | 0.093 | +0.069 [+0.063, +0.076] |
v3drrr |
domain (all) | 1.49 → 1.10 | 0.891 → 0.968 | 0.348 | -0.306 [-0.326, -0.288] |
v3drrr |
domain (clean) | 1.49 → 1.11 | 0.892 → 0.965 | 0.338 | -0.291 [-0.311, -0.271] |
v3drrr |
general (wikitext-2) | 9.73 → 10.43 | 0.519 → 0.507 | 0.093 | +0.070 [+0.063, +0.076] |
Checkpoints
main holds the best stage so far (by held-out evals) — currently the v3 weights above; mid-training checkpoints are not published to this public repo. Finished runs are backed up to the private repo hotocooo/Spark-X2.6-4B-Blender-checkpoints (v1/, warm_v4/, v5/, v3/, v6/, v6g/, v7/, v7a/, v7d/, v3d/, v3dr/, v3drr/): a local copy is deleted only after every uploaded file's sha256 matches the remote LFS object (small files: checked by size), and GGUFs are rebuilt on demand from the restored weights.
Quick start
from transformers import AutoModelForCausalLM, AutoTokenizer
repo = "hotocooo/Spark-X2.6-4B-Blender"
tok = AutoTokenizer.from_pretrained(repo, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(repo, trust_remote_code=True, torch_dtype="bfloat16", device_map="auto")
prompt = '### Task\nWrite a Blender 5 Python (bpy) script that builds this, with clean quad topology:\nA wooden stool with three legs and a round seat.\n\n### Script\n'
ids = tok(prompt, return_tensors="pt").to(model.device)
out = model.generate(**ids, max_new_tokens=4096, do_sample=False, top_k=0)
script = tok.decode(out[0, ids["input_ids"].shape[1]:], skip_special_tokens=True)
print(script)
Run the script in Blender: blender -b --python script.py or paste it into the Scripting workspace.
Requirements: transformers==4.57.1 (the architecture code ships with the repo and targets the 4.x API),
trust_remote_code=True. It is a completion model, not a chat model: use the prompt format below.
Prompt format
### Task
Write a Blender 5 Python (bpy) script that builds this, with clean quad topology:
<describe what to build>
### Script
Stop generation at the end-of-sequence token or at ###.
Anatomy base meshes
The anatomy data is procedural: anatomically proportioned joint skeletons (human: 7.5/7.2-head canons, male/female, slim/average/athletic/heavy builds, T-pose, A-pose, relaxed; hands: three phalanges per finger, opposable thumb, curl; quadrupeds: dog, cat, horse, deer, bear, digitigrade / unguligrade / plantigrade legs) wrapped by Blender's Skin modifier and smoothed with Subdivision Surface, the standard way to block in a sculpt-ready base mesh. Left/right symmetry is exact (mirrored in the data, not with a Mirror modifier, which would weld the centre line).
Examples above (front and side, wireframe shows edge flow):
- Make a low-poly-to-subdivided base mesh of a heavy female human body, 1.62 m tall (7.2 heads), in T-pose with anatomically correct proportions.
- Create a human left hand, 19 cm long, fingers flat and open, with three phalanges per finger. Use clean all-quad topology suitable for sculpting.
- Model a deer body (1.40 m from shoulder to rump), standing, unguligrade legs and a tail as a base mesh with good edge flow and no n-gons.
Training
Data
| Source | Rows | Notes |
|---|---|---|
| BlendNet (raw) | 12,059 | FreedomIntelligence/BlendNet @ 11a779c |
| BlendNet passing Blender check | 11,653 | ran headless in Blender, produced geometry |
| BlendNet train / held-out | 11,353 / 300 | held-out never trained on; evals use the first 100 |
| Procedural anatomy | 2,400 | human 1200, hand 400, quadruped 800; topology ≥ 0.9 |
| 3DCode procedural assets (train / held-out) | 2,266 / 977 | YipengGao/3DCode @ 0db485b, Infinigen-derived; ≤ 6144 tokens, ran in Blender; 20 whole object factories held out |
| Spatial / geometry / maths (train / held-out) | 2,800 / 160 | procedural: part assemblies placed by computed positions (Blender-checked size + part count) and worked QA on transforms, vertex / face counts, bounding boxes, vector geometry (Blender or closed-form checked) |
| Best-of-8 winners (bon_1) | 950 | self-generated, beat reference topology, fidelity ≥ 0.6 |
Method
Multi-stage, every stage full-parameter and verified in Blender:
- SFT (v1): full fine-tune on verified BlendNet + procedural anatomy, loss on script tokens only. (A long-context SFT stage on Infinigen 3DCode scripts was tried and dropped: it memorised the training factories and fell from 21/50 to 5/50 on held-out ones.)
- Spatial grounding (v3 onward): verified procedural data so placement is computed, not guessed (a script can run and still put every part in the wrong place): part assemblies, local-to-world transforms in Blender's scale / rotate / translate order, vertex / face counts through modifiers, bounding boxes, vector geometry. Trained in SFT and rewarded in RL (assemblies by shape match, QA by the verified answer).
- Rejection sampling (v3): sample 8 scripts per prompt, run all in Blender, keep the best only if it beats the reference's topology and matches the reference (stops "a lone cube" from winning).
- Reinforcement learning (v4): multi-turn GRPO-family policy optimisation (DAPO token-level loss, no KL term, truncated outputs masked) with the Blender verifier as the reward. Each prompt is an agent episode: the model writes a script, sees Blender's verdict (the error, or a scene report: parts, size, quad ratio, topology), then writes a better script or submits; the whole episode is one trajectory with Blender's text masked out. v3 is taught the loop first with best-of-N repair, refine and polish examples; a short SFT on the same rows plus restored submit examples (v3 itself saw none: every submit target became a realism polish) teaches stopping before RL. Side task adapted from CodeRL+: read a script, predict its object count and size, rewarded against what Blender actually builds.
- Selection: after each stage's held-out evals,
mainis replaced only if that stage beats every earlier one (currentlyv3); only the best model is kept.
No outputs of proprietary models (e.g. Claude, GPT, Gemini) are used as training data.
Topology score
On the evaluated mesh (modifiers applied), summed over objects:
score = 1.0·quad_ratio − 0.5·ngon_ratio − 1.0·non_manifold_ratio − 0.3·pole_ratio − 1.0·degenerate_ratio − 1.0·loose_ratio, each term a ratio (quads/faces, n-gons/faces, non-manifold edges/edges, bad poles/verts,
degenerate faces/faces, loose verts/verts). Bad poles are interior vertices with valence outside
3–5; 3- and 5-poles are normal in quad meshes and not penalised.
Hyperparameters
| Fine-tune type | full parameter (all weights), no adapters |
| Precision | bf16 weights, grads and optimizer state |
| Optimizer | AdamW (torch-optimi, Kahan summation), betas (0.9, 0.99), wd 0.01 |
| SFT learning rate | 1e-05, cosine, warmup 0.03 |
| SFT batch | v1 2 × 8; later stages 1 × 16 (chunked loss); effective 16 |
| SFT epochs | 1; max sequence v1 2048, later stages 6144 tokens; loss on script tokens only |
| Rejection round | 600 prompts × 8 samples, T=0.8, top-p 0.95; replay 2000 rows |
| RL | DAPO loss, group 8, lr 1e-06, on-policy (one update per rollout), KL β 0.0, 200 prompts + 25% execution-prediction prompts, truncated completions masked |
| Agent episodes | up to 3 scripts: after each, Blender's error or scene report; the model refines or answers SUBMIT (observations masked out of the RL loss, reward × 0.9 per extra script) |
| RL reward | 0.15·runs + 0.25·topology + 0.2·fidelity + 0.25·shape + 0.15·realism (0 if script fails) |
| Attention | fused SDPA (verified identical loss and greedy tokens vs. the reference eager path) |
| Hardware | Apple M4 Max, 64 GB unified memory, PyTorch MPS |
Reproducibility
Pinned: base model XHToken/Spark-X2.6-4B-Base @ e1bd659ae2b1c2263b2fc2544485976c1b516115, dataset FreedomIntelligence/BlendNet @ 11a779c7723af9bacbed371fccdc643ea20be446, transformers==4.57.1.
Pipeline: Blender-verified data filter, procedural anatomy generator, full fine-tune, best-of-N, GRPO, eval, export;
each stage resumes from its own checkpoints.
Eval generation: base model and v1 through transformers (bf16, MPS); later stages through llama.cpp llama-server on an f16 GGUF of the same weights (identical token ids, same sampler settings), 6.5x faster. Same prompts and Blender scoring throughout.
Limitations
- Base-mesh quality, not sculpts. Anatomy is blockout level (correct proportions and clean quads, ready to sculpt); muscle definition, faces and fine detail are beyond what a 4B script-writing model produces.
- Script, not mesh. The model writes code; geometry exists only after Blender runs it. Some scripts still fail, see the run rate above.
- Topology metric is structural. It rewards quads, manifold geometry and clean poles; it does not judge aesthetics. Fidelity checks size, proportions and part count against a reference, not exact shape.
- Open-world objects are weak. Outside the trained families (BlendNet-style props, furniture and products
from the generator kits, anatomy), a vague prompt such as "Make a banjo." mostly yields a stack of primitives
with the right part names in the wrong places. The Objaverse column's F-score
normdoes not show this: it rates a two-box "bed" above a box. The judge number beside it does: a clay render is ranked against real-object renders of all ~1,124 Objaverse-LVIS categories by a SigLIP image encoder, and the true category must land in the top 20. Kit-built objects reach rank 1-20; this model's held-out outputs sit at a median rank near 200. - English prompts, Blender 5.x API.
Safety
Generated scripts are executable Python. Run them in an isolated Blender, not one holding unsaved work.
Training and evaluation statically rejected scripts importing anything beyond bmesh, bpy, collections, colorsys, dataclasses, functools, hashlib, itertools, math, mathutils, numpy, random, statistics, types, typing, warnings or touching files,
network, add-ons or the app (DataSource, addon_enable, addon_install, export_scene, filepath, fromfile, genfromtxt, import_scene, libraries, load, loadtxt, memmap, open_mainfile, python_file_run, quit_blender, read_homefile, save, save_as_mainfile, save_mainfile, savetxt, savez, savez_compressed, tofile, url_open); getattr/setattr/delattr/hasattr were replaced by guarded versions
that refuse those names at run time. These filters are not a sandbox.
Special thanks
Special thanks to ACOTech for providing the machine behind this. Every step ran on it, locally: data verification, every fine-tune stage, the held-out evaluations and every benchmark on this page. The machine is a Mac Studio with an Apple M4 Max, 64 GB of unified memory.
How this was made
The data pipeline, fine-tunes, evaluation, benchmarks and this page were done with Claude Code running Claude Opus 5.5, directed and reviewed by hotocooo.
Citation
@misc{spark_x2_6_4b_blender,
title = {Spark-X2.6-4B-Blender: full fine-tune of Spark-X2.6-4B for Blender bpy modeling},
author = {hotocooo},
year = {2026},
url = {https://huggingface.co/hotocooo/Spark-X2.6-4B-Blender}
}
@misc{sparkx2.6,
title = {Spark-X2.6-4B: Advancing the Frontier of On-Device Agents for Real-World Tasks},
author = {SparkLLM Team},
year = {2026}
}
@misc{du2024blenderllmtraininglargelanguage,
title = {BlenderLLM: Training Large Language Models for Computer-Aided Design with Self-improvement},
author = {Yuhao Du and Shunian Chen and Wenbo Zan and Peizhao Li and Mingxuan Wang and Dingjie Song and Bo Li and Yan Hu and Benyou Wang},
year = {2024},
eprint = {2412.14203},
archivePrefix = {arXiv}
}
License: Apache-2.0, same as the base model and BlendNet. 3DCode is MIT; cite 3DCodeBench and Infinigen when using it. General-text replay uses FineWeb-Edu (ODC-By 1.0) by Hugging Face.
- Downloads last month
- 712
















