DMD Editor
DMD Editor
The DMD editor builds the animations that play on a table’s dot-matrix display — the panel on the backglass. A scene is a timed, layered document that cade folds into one frame per display: you stack layers, place text, live values, images, and animations onto the panel, give each one a start time and a duration, and watch the whole thing play back.
Everything you see on this page is composed by the engine, not drawn by the browser. The selection boxes, handles, and marks are chrome over cade’s own frame, so what the preview shows is what ships.
It shares the shared editor shell — the same drawer, toolbar, and rebindable shortcuts as the rules, lightshow, and sound editors. The Stage and Timeline themselves carry no node graph; opening an effect is the one place on this page that does. Reach the editor from DMD in the top bar.
The layout
There are no view tabs: the Stage and the Timeline are both on screen at
once, because where an element sits and when it appears are one decision. Drag
the ⋯ grip between them to re-split, or focus it and use Up / Down.
| Region | What it is |
|---|---|
| Stage (top) | The composed frame at the playhead — 128×32 on the shipped table — with a placement box drawn over each element |
| Timeline (bottom) | A ruler and playhead, one row per layer, one lane per element |
| Add element (left) | The palette of element kinds, with a filter box |
| Effects (left) | Every effect this machine declares — click one to open its node graph, + New effect adds one |
| Asset browser (left) | The asset catalog, collapsed until you open it |
| Preview (right) | The same frame again, chrome-free — the clean picture beside the annotated one |
| Scenes (right) | Every scene this table declares |
| Details (right) | The selected element or layer, with a live control for each of its parameters |
A breadcrumb reads Show › layer name when you have drilled into a layer; click a crumb to come back out. The toolbar carries Undo, Redo, and Copy link — which copies a link to exactly the scene, layer, and element you are looking at.
Scrubbing and playing
The ruler along the top of the Timeline is the scrub surface: drag on it and the playhead follows, snapping to a frame boundary so you land on a frame rather than partway into one. The grid is drawn at the display’s own refresh rate; if the engine doesn’t report one, no grid is drawn and nothing snaps. Scroll over the ruler to zoom about the pointer, Shift+scroll to pan.
The player controls sit in the Timeline’s own head corner, next to the playhead
they drive: ▶ play, II pause, a Loop checkbox, and a readout like
t=1.25s / 6.00s · loop. Space toggles play and pause from anywhere on the
page. Playback runs on wall time and does not snap — only the scrub gesture
does. There is deliberately no second scrub slider on the strip, since the ruler
already is one.
Layers
Layer rows are listed front-first — the order they actually composite in — so the reorder drag means what it looks like it means.
| Action | How |
|---|---|
| Add | + Layer, at the foot of the layer column. The form takes a name plus priority, blend, and opacity |
| Reorder | Press and drag the layer’s row; a drop line shows where it will land |
| Edit its params | The ⚙ button, or select the row and edit in Details |
| Rename | The ✎ button |
| Remove | The ✕ button — it confirms first, and it is undoable |
| Hide | The ■ / □ toggle |
| Fold | The fold arrow, which condenses all its elements onto one row |
| Drill in | Double-click the row, or press Enter on it |
Elements
| Kind | What it is |
|---|---|
| Text | A fixed string, resolved once when the show starts |
| Bound Value | A runtime-bound string re-evaluated every tick — a score that keeps climbing on the panel |
| Image | A still image, placed and sized in a pixel box |
| Animation | An animated GIF or sprite sheet, placed and sized like an image |
Add one by clicking its palette tile, by pressing the digit on the tile’s badge, or by dragging the tile straight onto the plane to place it where you drop it. Dropping off the plane cancels and writes nothing.
Select an element by clicking its Stage box or its Timeline mark. Shift- or Ctrl-click toggles more into the set, a rubber-band drag on empty plane replaces the set, and Escape clears it. Drag a box to move it — dragging one that is already in a multi-selection moves the whole group rigidly — and the eight handles resize it when that kind has a writable width and height. A move the engine refuses snaps back.
Everything else is in the Details rail: every parameter cade publishes for
that kind becomes a live control there. Rows are grouped under Placement
(layer, x, y, footprint, at, for) and Content (the rest). F2 renames;
Delete or Backspace removes the whole selection as one undo step.
A caption under the Stage explains the one piece of notation: a dashed green box marks a runtime-bound value, and the box is the footprint reserved for it. Scrubbing does not animate it, because its value is only known while the game runs. Underneath, a live problem list names anything placed off the plane or reserving more width than the panel has.
Double-click an Image or Animation element’s box — or press Enter
on a focused one — to open its art in the sprite editor.
The same dive-in sits in Details: the source row carries an Edit
sprite button once art is set, or Paint sprite when it isn’t. Every other
element kind’s double click does nothing — there is no properties dialog on
this page.
Painting a sprite
The sprite editor replaces the Stage and Timeline with a zoomable pixel canvas and its own tool rail, scoped to the one element you dove into. Escape, or the breadcrumb, returns to the layer; a dirty canvas confirms before it discards.
| Tool | Key | What it does |
|---|---|---|
| Pencil | P | 1 / 2 / 3 px brush — press again to cycle the size |
| Eraser | E | Paints transparent |
| Fill | G | Flood fill |
| Line | L | Two-point line |
| Rectangle | R | Outline — press again for filled |
| Ellipse | O | Outline — press again for filled |
| Eyedropper | I | Picks a colour off the canvas |
A left drag paints with the primary colour, a right drag with the secondary — an eyedropper right-pick fills the secondary slot the same way. Middle-drag pans, and holding Space turns a left drag into a pan too. Scroll zooms on the pointer, always by a whole pixel multiple so art stays crisp. X swaps the primary and secondary colours.
The palette offers the 16-shade DMD grey ramp first — the exact shades cade’s own dither maps art through, so a flat fill in one of them round-trips with no dither noise — plus a small starting colour set, your recent colours, and a free colour picker. Left-click a swatch for primary, right-click for secondary.
D toggles cade’s own decode overlay: the canvas starts showing the exact dithered frame the display will render — not a showcade approximation — while painting keeps landing in the full-colour raster underneath. The dither pattern depends on the element’s true position on the panel, so this is the one view that tells you what the art will really look like once it ships.
Undo and redo (the usual chords) are local to the sprite — a separate stroke stack, not the document’s own history — so they never touch anything outside the canvas you’re painting.
Animating
Open an Animation element and the Timeline host becomes a frame strip instead, with its own transport: play/pause, a step button each way, an FPS field, and Loop. Each frame carries duplicate (⧉) and delete (✕); + appends a blank one, and dragging a thumbnail reorders. A sequence holds up to 240 frames.
N toggles onion skinning — the previous and next frame ghosted under the canvas at low opacity, to help line up motion. It only arms in animation mode; a still has no neighbours to ghost.
An animation element’s existing GIF source decodes frame-by-frame automatically in a Chromium-based browser; elsewhere you get the first frame only, with a notice saying why. Saving never writes a GIF back out — it always converts to a plain frame strip, so playback doesn’t depend on the browser that saved it.
Saving
Ctrl+S (or the header’s Save button) encodes the raster, uploads it, and commits the element’s source as one step. Painting is the one place on this page that does not commit stroke-by-stroke the way everything else does — strokes are a local draft until Save lands, which is what the discard confirm on Escape is protecting. Saving needs the same sign-in as uploading any other asset; signed out, Save states that and refuses.
Effects
Effects in the left drawer lists every effect this machine declares. + New effect names one and opens it; a row’s ✎ renames and ✕ removes it (confirmed first, undoable after).
Opening an effect swaps the Stage and Timeline for a small node graph: nodes grouped Inputs, Transforms, Compositing, and Temporal, wired together into a single Output that’s created for you and never added by hand. A band above the graph shows a live preview of the result on the display plane, with its own scrub transport (play, scrub, loop) — it re-evaluates and re-decodes on every edit, so what you see is always the real engine’s rendering of the graph as it stands.
Building the graph itself follows this page’s usual rule — add a node, wire a connection, drag a param, and it’s committed, no save button. Turning that graph into something usable elsewhere is the one deliberate extra step:
- Bake & apply renders the graph to a frame strip, uploads it, and rewrites every element already playing this effect to point at the new strip. An amber badge on the button, and on the effect’s row, means the graph has changed since the last bake.
- Insert as element adds a new Animation element playing the baked strip into the layer you last drilled into (or the show’s first layer, if none). It asks you to bake first if the effect never has been.
Baking needs the same sign-in as any other upload. Signed out, Bake & apply still runs and commits inside the engine, but nothing is kept in the asset catalog until you sign in and bake again.
Hiding a layer or an element
The ■ / □ toggle on a layer row and on an element row are both a real, undoable edit: a layer’s sets its opacity to 0, an element’s sets its own visibility off, and un-hiding clears that back to cade’s own default rather than guessing at a prior value. Either way the art disappears everywhere that show plays — and hiding something doesn’t deselect it, so Details stays bound to it, which is how you find it again to un-hide.
Hiding a layer hides every element inside it too, whatever each element’s own toggle says — but each row only ever reads and writes its own flag, so flipping one element’s toggle inside an already-hidden layer changes nothing on the Stage until the layer itself comes back.
Starting a show
Each row in Scenes shows the triggers that start it as chips (⊳ device.spinner_001.hit, with a ? when it carries a guard). + adds one —
the form asks when (type freely against a suggestion list of cade’s events,
or click Browse… to drill through them by category instead) plus an
optional condition — and ✕ removes one. A scene with none reads no
trigger — inert, and a show can always be started from a scoring rule
instead.
The P number on a row is the fold order against the other scenes on the same
display: higher sits on top. Clicking a row opens that scene; double-click or
right-click it for its settings — duration, loop, device, priority, blend,
opacity, and its name.
A scene’s device is an authoring-time default so the editor has geometry to preview against. Play time still decides which display it lands on.
There is no Save button
Every gesture commits immediately: the edit goes to the running engine, cade validates it and rewrites the table’s DMD configuration, and the editor re-reads the result. If cade refuses, nothing is written and the refusal is stated out loud. Undo and Redo restore that configuration’s previous text, so they are a real source-level history rather than a stack of inverse gestures — you can read and edit the same text directly on the Source page.
The edited configuration is kept as a draft in your browser, re-adopted on the next load, and pushed to other open windows, which repaint.
Painting a sprite is the one exception: strokes are a local draft until you explicitly Save. An effect’s node graph is not an exception — it commits per edit like everything else here — but turning that graph into a usable asset still takes the explicit Bake & apply step; see Effects.
The status line
The status line at the foot of the window is where this page says what it is doing, always prefixed DMD ·. It has three registers, and they look different on purpose:
- Ordinary grey for a statement of fact —
DMD · Attract. - Amber for a degraded frame —
DMD · Attract — rendered without 2 elements whose art could not be loaded: back/backdrop, front/logo. A missing image costs one element, never the whole frame, and playback stays available. The Preview section repeats the notice under the picture it explains. - Red for a refusal, which is announced rather than just displayed.
Fixture mode
If no engine answers, or the engine answers but the table declares no scene, the page still opens — against a bundled sample scene called Attract. The status line names which of the two it is, because they call for different fixes:
No cade engine answered — this page needs the full engine build, which carries the display-show exports.
This machine declares no
display_showblock, so there is no authored show to open.
In fixture mode the transport is disabled with its reason attached, and every edit refuses out loud rather than quietly doing nothing.
Related: a control the engine could only ever refuse is hidden rather than shown greyed. If + Layer or a layer row’s ⚙ ✎ ✕ are absent, this engine publishes no such operation — a grey control would read as broken instead.
Signing in
The editor itself needs no account. The only thing gated is uploading into the Asset browser: browsing and using what is already in the catalog works signed out. Only image rows can be dragged out of the browser — dropping one onto an existing element retargets its art, dropping on open plane creates a new element, and holding Shift always creates.