Building Blocks
Every object on the board that is not one of the built-in shapes is composed from building blocks: a small, closed set of approved primitives, modifiers, behaviours and components held in one registry. A clock, a compass, a speedometer, a thermometer and a steering wheel are all the same circles, arcs, lines, text and rotations, arranged differently. Nothing draws outside the registry, and no object carries code.
The five layers
A block is a named, registered building unit with a declared input schema. There are four kinds, and objects made of them are the fifth.
| Layer | What it is | Examples |
|---|---|---|
| Primitives | The things that actually draw. Each one is a pure function from resolved values to one SVG tag. | circle, rect, ellipse, line, polyline, polygon, arc, ring, path, text, image, group |
| Modifiers | Transforms and paint applied to a node and everything under it. | translate, rotate, scale, mirror, opacity, visibility, stroke, fill, z-order, repeat |
| Behaviours | What the reader can do to a node, and what that writes. | clickable, drag-angle, drag-rotate, drag-circle-point, press-and-slide, drag-axis-tick, keep-time, follow-pointer, remember, track-pointer, tooltip |
| Bindings | Where a value comes from, declared rather than computed. | constant, parameter, variable, expression, formula, token, format, choose, concat, direction, memory, termUnit, termName, model |
| Components | Ready-made assemblies that compose the layers above, from a tick ring to a whole clock. An object is itself a component, so one can be reused inside another. | dial-face, tick-ring, label-ring, pointer-hand, key-cap, seven-segment-display, plot-grid, plot-axes, plot-crosshair, memory-list, memory-trace, clock, compass, speedometer, circular-gauge, rotating-vector, orbit-system, steering-wheel, thermometer, calculator, mouse-tracker, ruler, protractor, trigonometric-circle |
How a drawing is made
The spine is the same for every object on the board. The shape holds the values, the compiler turns the definition into render nodes, and the renderer writes the SVG once.
| Step | What happens |
|---|---|
| 1. The shape | A ComponentShape on the board holds the object's parameter values in its own properties — which is why an object gets undo/redo, copy/paste, collaboration and saving with nothing written for the purpose. |
| 2. The compiler | BlockCompiler evaluates the definition's locals, drops the children whose when is false, expands repeat, resolves every binding and walks down to primitives. |
| 3. The renderer | BlockRenderer builds every tag itself and escapes every value. No markup from a definition ever reaches the page. |
| 4. The interaction | A behaviour writes exactly one command — a property of the shape, or a model variable through the calculator — and the next compilation reads it back. That is why a whole drag is a single undo entry and why no object has a redraw path of its own. |
Values that change on their own — a model variable, the iteration on screen, the object's own size — arrive as bindings, so a definition never subscribes to anything or schedules a redraw. Bind a property to a variable and the object follows the simulation.
eval anywhere on this path, and no JavaScript in a definition. Formulas are Modellus LaTeX, parsed by the same parser the expression shapes use. A block type the registry does not hold is refused by the compiler and by the validator alike.
An object is one JSON document
A new object is a single JSON document: no JavaScript, no markup, no file to wire up. It declares what the reader can edit (parameters), what it works out for itself (locals) and what it draws (root).
{
"schemaVersion": "1.0.0",
"type": "my-object",
"category": "component",
"displayName": "My object",
"description": "One sentence, written for the person choosing it from the palette.",
"icon": "fa-light fa-circle-notch",
"tags": ["object"],
"parameters": [ … ],
"locals": [ … ],
"root": { "id": "my-object", "type": "group", "children": [ … ] }
}
The same document is what the editor bundles, what the community catalogue publishes, what the assistant invents and what a model carries with it. Nothing about the object depends on which way it arrived. The full recipe, field by field, is on Object Definition.
Where an object comes from
| Source | How it gets there | Who sees it |
|---|---|---|
| Bundled | A JSON file the editor ships with | Everyone, offline included |
| Community catalogue | Published under Assets → Objects, fetched when placed | Anyone whose palette reads the catalogue |
| Invented on a board | The assistant's save_custom_component, or Copy definition from an object's settings menu | The model that uses it |
| Carried by a model | The model file's own objects section | Whoever opens that model, anywhere |
An object the editor does not ship with travels inside the model that uses it, so a board can invent one and a colleague can open the file and see it. A document naming a type the editor already ships is refused, deliberately: a model cannot freeze a bundled object at an old version.
Three ways to build one
| Way | What you do |
|---|---|
| Start from an object that exists | Place one from the Objects palette, set it up, then Copy definition in its settings menu and edit the JSON. |
| Write the document | Follow Object Definition and paste the result into the object editor in the catalogue, or bundle it with the editor. |
| Ask the assistant | Describe the object. The assistant composes it through the block tools, validates it and inserts it — and what it produces is an ordinary object, fully editable. |
Drawing rather than writing is the fourth way in for the parts that are illustration: an SVG file can be converted into primitive nodes and pasted into the document, wired by the ids in the drawing. It is converted, not embedded — nothing new reaches the renderer.
The look is not the object's to invent
An object drawn from blocks has to look like the board it stands on, and the board already has a look: the font it writes in, the grey its axes are drawn in, the length of a tick, the dash of a crosshair, the plate a value is read on. Those numbers are the design tokens, and the chart, the ruler and the referential read the same ones — so an object drawing against a scale is the same grey as a chart beside it.
Two rules follow, and together they mean no definition ever names a font or an axis colour:
- A colour, a size or a width is written as a token reference —
"token:stroke.accent", or the binding{ "token": "font.size.tick" }— never as a literal. - Anything cartesian is a component, not a drawing:
plot-grid,plot-axesandplot-crosshaircarry the board's own nice ticks, minor ticks, label spacing and the drag handles that rescale an axis.
Because the tokens are one table, a preset restyles every object at once. Five ship with the editor: standard, minimal, scientific, classroom and high-contrast, and every value in each is listed on Block Reference.
What an object can do
| Capability | How |
|---|---|
| Read the model | A variable binding, or an expression over model variables. The object redraws with the simulation. |
| Scale with its box | The reserved parameters $width and $height, read through a local, so geometry is worked out from the size the reader dragged. |
| Be driven by hand | drag-angle, drag-rotate, drag-circle-point, press-and-slide, clickable and drag-axis-tick write a model variable — or the object's own parameter when the row holds a plain number instead of a term. |
| Refuse to be driven | Nothing switches interaction on or off: a row naming a term the model works out for itself is read-only, and says so with the cursor a locked handle uses. |
| Remember | A memory parameter holds rows of { text, x, y }, filled by remember, track-pointer or a key, and read back by memory-list or memory-trace. It lives in the shape, so the file carries it and undo restores it. |
| Replay a recording | A memory declaring termParameters makes row n iteration n of those terms, so the board's player replays it and everything bound to them moves along. |
| Follow the pointer | follow-pointer reports where the cursor is into parameters the definition names, without writing anything down — no edit, no undo entry, no changed file — and stops while the player runs. |
| Count real time | keep-time runs a clock of its own, so it goes on counting while the model stands still, and the whole run is one undo entry. |
What is checked before an object is drawn
A definition passes two gates. Registration reads the document; validation reads the tree it compiles to. An object cannot be published from the catalogue, inserted by the assistant or saved as a component until both are clean.
| Gate | Refuses |
|---|---|
| Registration | An unsupported schemaVersion, a type outside ^[a-z][a-z0-9-]{2,48}$ or already shipped, a category other than component, a missing root, a parameter or local without an id, a local colliding with a parameter or an earlier local, and any formula reading a name the document does not declare. |
| Validation | Unknown block types, bindings on properties that do not accept them, variables that do not exist (with the nearest name suggested), expressions that do not parse, the runtime limits, and a visual pass: nothing drawn, everything invisible, zero-size interactive targets, nodes far outside the box. |
The runtime limits are 2 000 nodes, 16 levels of nesting, 8 levels of component nesting, a repeat count of 720, 512 characters per expression and 2 000 memory rows. Image sources are limited to https:, data:image/ and relative paths; path data must match the SVG path grammar.
Where to go next
Object Definition
The document a new object is written as, field by field, with a worked example.
Block Reference
Every block in the registry with its properties, defaults and ranges.
Objects on the board
What the objects built this way do, and how the reader works them.
AI Reference
The model format and the tool surface an agent composes an object through.