← Research

Open Release · 02

An open-vocab robot arm you drive with words.

A vision-language-action policy maps what it sees and what it's told into what it does. This one is tiny enough to run its whole forward pass on-device, its language is open-vocabulary, type any instruction and a real language model embeds it on your device, and it controls an articulated arm through its joints. Tell it which block to reach and watch it look, then move. No server, no cloud. But a policy carries no guarantee before it acts, the missing move is the certificate that gates the action on its own energy before it commits (Energy First Architecture).

The architecture, seen

A vision-language-action policy, drawn in full.

Before the demos, the shape of the thing. A VLA is three streams that converge: a camera frame runs through convolutions, an instruction becomes a language embedding, and the two fuse to locate the named block, then the arm reaches for it. Below is exactly that, in 3D, as a real network trained on your device: every node is a neuron, every glow a live activation. Tell it which block, and watch the vision light up, the language pick the target, and a crosshair mark where it found it. It is the architecture of the released nano-vla-arm, shrunk so you can watch it think.

Switch red and green and the gold crosshair jumps to the block you named, the policy localizing its target from pixels alone, to within a few percent of the frame, and the arm reaching the spot it marked. Every node is real and the whole forward pass runs on your device. Locating the named target, then reaching it, is one way a VLA works.

Loading the released policy…
Multi-step

Now make it manipulate: “stack the red on the green.”

Reaching is one step. This policy does two: it grasps the block you name, carries it, and places it on the other one, a real manipulation sequence. The held block is drawn at the gripper, so the policy can see which phase it's in. One honest detail: mean-pooled sentence embeddings barely encode word order (“red on green” and “green on red” land at ~0.67, near chance), so the source block is parsed from the instruction and the policy is conditioned on the resolved source and target.

Loading the released policy…

Held-out, full-episode: it grasps, carries, and stacks the correct block 94.0% of the time. Flip which block is the source and success against the original target drops to 22.8%, the language is doing the work. 55,666 parameters; the whole two-phase forward pass runs in plain JavaScript. Weights: ↗ nano-vla-stack.

Real pixels

Trained on the actual sim, not a drawing.

Every policy above learns from a renderer we wrote. This one learns from the real thing: 2,500 frames captured live from the Forge MuJoCo arm running on WebGPU, each paired with the action the checkpoint took. It reads three stacked frames, motion matters, and predicts the joint move from pixels alone. On held-out reaches it explains 82.9% of the checkpoint's action variance (46.6% from a single frame; the jump is the velocity you can only see across frames).

Loading the pixel replay…

This panel is a replay on captured held-out frames, which is how the policy was evaluated. The live version, the same weights closing the loop on the sim's own render, is directly below. Weights + the capture recipe: ↗ nano-vla-pixels.

The loop, closed

A policy driving the simulator it is looking at.

Nothing here is captured and nothing is replayed. The arm renders, the page reads that render back, the policy looks at it, and the joints move, about ten milliseconds, on your device, with no joint angle and no target coordinate anywhere in its input. The checkpoint drives by default so you can watch the two disagree in real time. Press the button and the pixels take the arm.

Booting the live pixel view…

We had written this off as impossible, and half of that was right: a page cannot read its own WebGPU canvas, during the render loop the 2D path comes back blank, which we re-confirmed. The way through is a render target rather than the canvas: draw the same scene into one and readRenderTargetPixelsAsync returns true pixels. Three corrections put those pixels in the policy's distribution, de-pad the rows (WebGPU aligns them to 256 bytes), encode to sRGB (the target renders linear), and match the canvas's antialiasing. Against a real capture of this canvas that lands at MSE 0.0037; flipping the rows or skipping the sRGB step lands 10 to 16 times worse.

Once the readback worked we could do something the old capture pipeline could not: train the policy on the very frames it would run on, collected through this readback path with the expert's action perturbed so the arm visited states a clean demonstration never contains. Train and live stopped being two distributions. Driving from pixels alone, this policy now completes about 23 reaches a minute, the state-based expert it was cloned from does 21. It matches its teacher, seeing only the picture the teacher never needed.

That last number cost us an honest lesson, so here it is. For most of this work the same policy scored about one reach a minute and read like a hopeful failure: a student, not the expert. The cause was not the policy. The loop re-perceived every 90 milliseconds, a constant typed early and never questioned, so the policy decided at roughly 11 Hz while the expert re-decided every frame at 60; its action went stale between glances and the arm overshot. Setting the interval to 30 milliseconds (still 9 ms of real work, nowhere near saturated) took it from ~1 to ~23 reaches a minute. One arbitrary number had been the ceiling the whole time, and it had silently poisoned every experiment run through it: models we had ranked as broken and as best were, measured correctly, the same. What actually moved the needle was the readback capture, ~10 reaches a minute up to parity; several rounds of the fancier data-collection we had credited moved nothing. The full corrected scoreboard, including what we got wrong: ↗ nano-vla-pixels-dart.

The frontier one, live

Now run the real 450M SmolVLA.

The policy above is tiny, to teach the idea. But the actual frontier is also runnable here: SmolVLA, Hugging Face's 450-million-parameter vision-language-action model, running its full flow-matching pipeline entirely on your GPU through WebGPU. No server touches it. The weights (831 MB, fp16) stream from our Hugging Face repo, onnxruntime-web runs them on WebGPU, and it produces a real 50-step action trajectory. Verified end-to-end.

Loading the SmolVLA runner…
What a VLA is

See, be told, act.

Three modalities in one policy. This one is built so it genuinely needs all three.

Vision · perspective camera

It sees the arm and the blocks

A 40×40 perspective camera view: a receding tabletop with a 2-link arm, a gold gripper, and a red and a green block at positions that change every scene. Foreshortened, camera-like, not a flat schematic. It has to look to find them.

Language · open-vocab

It obeys what you type

Any instruction works. It's embedded on your device by a real language model (all-MiniLM), the same encoder used at training time, so paraphrases it never saw, “grab the green one”, “head toward crimson”, still land.

Action · joint control

It drives the arm's joints

Out come two joint commands. The arm is stepped forward and the loop closes, the whole forward pass, three convolution layers and all, in plain JavaScript. No server, no cloud.

The numbers

Tiny, and measured.

Evaluated closed-loop, driving the arm joint by joint, on held-out scenes, with instructions it never trained on.

119,986policy parameters
97.0%correct on novel instructions
97.0%correct on trained instructions
0.0%correct when the instruction is flipped

The novel-instruction number is the open-vocab proof: unseen phrasings still work, because the on-device text encoder maps them near the ones it learned. The flipped number is the language-grounding proof: with the wrong instruction it drives to the other block, so success at reaching the named one collapses to zero. It was trained by behavior cloning on a scripted Jacobian-IK expert and made robust to closed-loop drift with DART-style state-noise injection, the same idea as DAgger. A small reference policy on a rendered task, not a foundation model.

Get it

Weights, and the family it belongs to.

Model

nano-vla-arm-3d

Weights, config (incl. the baked perspective homography), and the model card. The flat-camera predecessor is nano-vla-arm.

↗ Hugging Face

First checkpoint

forge-arm-reach-bc

Our first released policy: the state-based reach controller, runnable on the page.

→ Run it

Build one

Vision-Language-Action

The hands-on course: make vision, language, and an action head yourself, the exact arc behind these models, honest findings and all.

→ Take the course