Skip to content

DSL Overview

The @elucim/dsl package lets agents and content pipelines describe Elucim visuals as JSON or YAML documents instead of JSX. Elucim documents are normalized scene graphs with explicit elements, timelines, and optional state machines.

Every DSL document is an ElucimDocument object:

import type {
ElucimDocument,
ElucimElement,
ElucimMetadata,
ElucimScene,
ElucimStateMachine,
ElucimTimeline,
} from '@elucim/dsl';
interface ElucimDocument {
$schema?: string; // Optional JSON Schema URL for editor autocomplete
version: '2.0'; // Current document format version
scene: ElucimScene; // Root scene/player settings
elements: Record<string, ElucimElement>;
timelines?: Record<string, ElucimTimeline>;
stateMachines?: Record<string, ElucimStateMachine>;
defaultStateMachine?: string;
metadata?: ElucimMetadata;
}

The scene chooses the render surface and references top-level element IDs. Elements are stored by ID so timelines and state machines can target stable objects directly.

{
"version": "2.0",
"scene": {
"type": "player",
"width": 800,
"height": 600,
"fps": 30,
"background": "#0a0a1e",
"children": ["orb"]
},
"elements": {
"orb": {
"id": "orb",
"type": "circle",
"props": {
"type": "circle",
"cx": 400,
"cy": 300,
"r": 60,
"stroke": "$accent",
"strokeWidth": 3,
"opacity": 0
}
}
},
"timelines": {
"intro": {
"id": "intro",
"duration": 30,
"tracks": [
{
"target": "orb",
"property": "opacity",
"keyframes": [
{ "frame": 0, "value": 0 },
{ "frame": 30, "value": 1 }
]
}
]
}
},
"defaultStateMachine": "main",
"stateMachines": {
"main": {
"id": "main",
"entry": "intro",
"states": { "intro": { "timeline": "intro" } },
"transitions": [
{ "id": "entry-start", "from": "entry", "to": "intro", "trigger": "onStart" }
]
}
}
}

The same document in YAML — parsed with fromYaml():

version: "2.0"
scene:
type: player
width: 800
height: 600
fps: 30
background: "#0a0a1e"
children: [orb]
elements:
orb:
id: orb
type: circle
props:
type: circle
cx: 400
cy: 300
r: 60
stroke: "$accent"
strokeWidth: 3
opacity: 0
timelines:
intro:
id: intro
duration: 30
tracks:
- target: orb
property: opacity
keyframes:
- { frame: 0, value: 0 }
- { frame: 30, value: 1 }
defaultStateMachine: main
stateMachines:
main:
id: main
entry: intro
states:
intro: { timeline: intro }
transitions:
- { id: entry-start, from: entry, to: intro, trigger: onStart }
import { fromYaml } from '@elucim/dsl';
const doc = fromYaml(yamlString);
// doc is a validated ElucimDocument

Every node has a "type" field. The full set of supported types:

SectionDescription
sceneRoot scene/player settings with top-level child element IDs
elementsID-keyed primitive/group definitions
timelinesExplicit property tracks and keyframes
stateMachinesEntry/state/transition controllers for interactive playback
TypeDescription
circleSVG circle (cx, cy, r, fill, stroke)
lineSVG line or straight connector (x1, y1, x2, y2, lineStyle, startCap, endCap)
bezierCurveSmooth connector/path (x1, y1, cx1, cy1, cx2, cy2, x2, y2, startCap, endCap)
rectRectangle (x, y, width, height, rx, ry)
polygonPolygon from a points array
textText label (x, y, content, fontSize)
TypeDescription
axes2D coordinate system with ticks and optional grid
functionPlotPlot a math expression as a curve
vectorArrow from origin to a point, with optional label
vectorFieldGrid of vectors computed from an expression
matrixBracketed matrix display
graphNode-and-edge graph visualization
latexLaTeX expression rendered via KaTeX
barChartLabeled bar chart

Animation is represented by timeline tracks and state-machine transitions, not wrapper nodes like fadeIn or sequence. This keeps generated documents inspectable and gives the editor/viewer a single motion model.

  • DslRenderer — render a document in React
  • Validation — validate documents before rendering
  • Agent documents — author interactive timeline flows
  • Themes — semantic color tokens ($accent, $foreground, etc.)