Gaitkit Open the lab
Docs

Specimens,
opened up

Every Gaitkit specimen is a small JavaScript module that builds a three.js group. This page covers the format, the knobs, how things move, and how to take them into your engine. 518 specimens in 51 kits follow it, checked by the same validator on every change.

Format v0three.js r150+Max 6,000 triangles (largest today: 5,020)

01Quick start

Paste this into an HTML file and serve it. It loads a cottage with brick walls and a porch from the CDN.

index.html
<script type="importmap">
{ "imports": { "three": "https://cdn.jsdelivr.net/npm/three@0.180.0/build/three.module.js" } }
</script>
<script type="module">
import * as THREE from 'three';
import { createAsset } from 'https://gaitkit.com/cdn/cottage.mjs';

const scene = new THREE.Scene();
scene.add(new THREE.HemisphereLight('#ffffff', '#8a8f80', 2.5));
const cottage = createAsset({ walls: 'brick', porch: true });   // every knob is optional
scene.add(cottage);                                              // rests on y = 0, front faces +Z
</script>

Not sure which knob does what? Open the specimen in the lab: it shows every knob, and the code tab writes the createAsset() call for what you see. Agents get the same through MCP.

02What a specimen is

One ES module per specimen, with four exports. No mesh is drawn by hand; GLB and OBJ files are built from the module.

assets/spider-bot.mjs (shortened)
export const meta = {
  id: 'spider-bot', name: 'Spider Bot', class: 'mech', kit: 'Mech Hangar', legs: 8,
  tags: ['spider', 'robot', 'arachnid', '8 legs', 'sci-fi', 'drone'],
  describe: 'A knee-high eight-legged robot spider with a glowing eye cluster. Walks with a wave gait
             and keeps its feet planted on uneven ground.'
};

export const params = {
  colorway:  { type: 'choice', options: ['steel', 'hazard', 'toxic', 'ivory'], default: 'steel',
               describe: 'Named palette for body, legs and eyes: steel (grey/red), hazard (black/yellow), toxic (green), ivory (white/blue).' },
  legLength: { type: 'range', min: 0.75, max: 1.4, step: 0.05, default: 1,
               describe: 'Scales both leg segments. Higher = taller, longer stride, more spindly.' },
  eyes:      { type: 'choice', options: [2, 4, 8], default: 4, describe: 'Number of glowing eye lenses on the head.' },
  turret:    { type: 'toggle', default: false, describe: 'Mounts a small dorsal sensor turret on the abdomen.' }
};

export const presets = { 'lab drone': { colorway: 'ivory', turret: true } };
export const night = { eye: { zone: 'eye', intensity: 1.2, describe: 'Eye lenses glow after dark.' } };

export function createAsset(params = {}) { /* builds and returns a THREE.Group */ }
  • meta: id, name, class (creature, mech, vehicle, building, nature, prop, character), kit, tags, leg count, and one sentence an agent can act on.
  • params: the knobs. Every one has a describe written for people and agents.
  • presets: named knob combinations, one click in the lab.
  • night: which parts glow after dark (optional). A zone without a colour glows in the part's own colour.
  • createAsset(params): builds the group. With no arguments it always builds the same published look.

03Knobs and presets

TypeFieldsUsed for
choiceoptions, defaultNamed looks or parts: a colourway, a roof type, a weapon. The describe text can explain each option in brackets, and the matcher learns those words.
rangemin, max, step, defaultSizes, lengths and counts. A step of 1 makes it a count ("3 floors").
toggledefaultA part that is there or not: a porch, a horn, a turret.
colordefault as #rrggbbAny colour. Colour words like "dark green" map to it.

Pass only the knobs you want to change; the rest keep their defaults, and unknown keys are ignored. Stay inside each range: the validator only checks the published bounds. Each specimen's schema is also published as /cdn/{id}-params.json.

04Scale, orientation, budget

  • Y-up, metres, real-world scale. A door is about 2 m, a car about 4 m, a beetle a few centimetres unless a knob says giant.
  • Rests on y = 0, centred on x and z. Front faces +Z; for walkers, front is the direction of travel.
  • One material: flat-shaded vertex colours, one draw call after merging. GLB exports split it into one material per colour for engines that prefer that.
  • At most 6,000 triangles, so whole kits stay light enough for phones and Quest.
  • Deterministic. Randomness comes from a seeded generator, never Math.random; a seed knob gives you new rocks or trees on demand.

05Walkers

Creatures, mechs and characters with meta.legs > 0 walk with two-bone IK. Each leg is a named chain leg_i > hip_i > knee_i > foot_i, and userData.gait holds stride, duty factor, lift, phases and foot home positions.

three.js
import { createAsset } from 'https://gaitkit.com/cdn/spider-bot.mjs';
import { walker, raycastGround } from 'https://gaitkit.com/runtime/walk.mjs';

const spider = createAsset({ legLength: 1.25, eyes: 8 });
scene.add(spider);
const gait = walker(spider, { ground: raycastGround([floorMesh]) });   // omit ground for a flat floor

let dist = 0;
renderer.setAnimationLoop(() => {
  dist += speed * clock.getDelta();   // metres
  spider.position.z = dist;           // move the model yourself...
  gait.poseAt(dist);                  // ...and pose the legs for that distance
  renderer.render(scene, camera);
});

The pose is a pure function of distance travelled, not time. That is why a planted foot stays planted at any frame rate, in a scrub bar or in an offline render; the validator rejects any specimen whose planted foot slides more than 1 mm.

Knees bend up (insects, spiders, crabs), forward (a mammal's front leg) or back (hocks, birds, reverse-jointed mechs). To re-implement the IK in another engine, use the pole vector (0, 0, ±1) in body space and the per-leg knee field from get_asset().gait.segments.

06Wheels, wings and hinges

Vehicles

three.js
const car = createAsset();                // meta.motion === 'drive'
// each frame
dist += speed * dt;
car.position.z = dist;
car.userData.tick(dist);                   // wheels turn exactly dist / radius

Flyers and swimmers

The model still rests on y = 0; userData.altitude says how high to hold it.

three.js
const bird = createAsset();               // meta.motion === 'fly' | 'swim'
// each frame
dist += speed * dt;
bird.position.set(x, groundY + bird.userData.altitude, dist);
bird.userData.tick(dist);                  // wings beat (or the tail waves) with the distance

Hinged and sliding parts

Doors, lids, drawers, turrets and hatches are named groups with their pivot on the hinge, listed in userData.rig as rotate, slide or spin with an axis and a range.

three.js
const chest = createAsset();
// userData.rig = { lid: { type: 'rotate', axis: 'x', range: [-110, 0] } }
const lid = chest.getObjectByName('lid');  // pivot sits on the hinge
lid.rotation.x = THREE.MathUtils.degToRad(-80);

07Night lights

Specimens with windows, lamps, eyes or engines export night: each entry names a zone, and every mesh whose own name or a parent's name is that zone (or starts with zone_) glows in its colour. Add your own point lights where you want real light; the zones only set what looks lit.

three.js
import { night } from 'https://gaitkit.com/cdn/street-lamp.mjs';
const zones = Object.values(night);        // [{ zone: 'lamp', color: '#ffd38a', intensity: 1.4 }]
lamp.traverse(o => {
  if (!o.isMesh) return;
  const z = zones.find(n => [o, ...ancestors(o)].some(a => a.name === n.zone || a.name.startsWith(n.zone + '_')));
  if (!z) return;
  o.material = o.material.clone();
  o.material.emissive.set(z.color);
  o.material.emissiveIntensity = z.intensity ?? 1;
});

08Engines

The module is the asset: import it from the CDN or copy it with runtime/ next to it. Map the bare three import with an import map or your bundler; r150 and later work.

JavaScript
import { createAsset } from 'https://gaitkit.com/cdn/horn-beetle.mjs';
const beetle = createAsset({ shell: 'scarlet' });
scene.add(beetle);

09Characters

People and humanoids are built on one skeleton with a style knob (realistic, stylized, chibi, blocky). Their parts are rigid (no skin weights), and the GLB's root node carries a Humanoid bone map in extras.humanoid: Hips → body, Spine → spine, Head → head, LeftUpperLeg → hip_0, LeftHand → hand_L and so on, 21 bones in all. Unity's Humanoid avatar, Godot's SkeletonProfileHumanoid and Blender rigs use the same names, so you can retarget onto them.

10Files

FileWhat is in it
/cdn/{id}.mjsThe module itself, with every knob. Imports three and /runtime/kit.mjs.
/cdn/{id}.glbThe default look, one material per colour, with a Walk or Loop clip when it moves.
/cdn/{id}.objStatic geometry, no colour yet.
/cdn/{id}-params.jsonKnob schema, presets and night zones.
/catalog.jsonEvery specimen's metadata: size, palette, knobs, gait, rigged parts, file links.
/runtime/walk.mjswalker() and raycastGround(), free to ship with your project.

Downloads from the lab bake the knobs you set into the GLB. Use of all files is covered by the licence.