How Eido Works

From data to pixels — a tour of the rendering pipeline

The Big Picture

A scene in Eido is a plain Clojure map. Eido's job is to let you author that map with comfortable, composable functions — then hand it to a fast native rendering engine that turns it into pixels. The authoring layer is pure data in, data out; the rendering engine is a separate native backend Eido drives through a thin bridge.

Scene Map
your data
Translate
to engine grammar
Render
validate + rasterize
Output
PNG / GIF bytes

Everything up to the render call is inspectable Clojure data — you can print it, diff it, serialize it. The rendering engine is encapsulated: you need a JVM, not a separate install, and the same engine renders whether you call it from a script, the REPL, or a batch job.

Step 1: The Scene Map

Everything starts here. A scene is a map with three keys — the canvas size, a background color, and a vector of nodes. Each node is itself a map describing a shape, a group, or a generator:

{:image/size [400 300]
 :image/background [:color/name "linen"]
 :image/nodes
 [{:node/type     :shape/circle
   :circle/center [200 150]
   :circle/radius 80
   :style/fill    [:color/name "coral"]}
  {:node/type     :shape/rect
   :rect/xy       [50 50]
   :rect/size     [100 60]
   :style/fill    [:color/name "steelblue"]}]}
Rendered output

That's the entire input. No classes, no builder patterns, no inheritance. Nodes can be shapes (:shape/circle, :shape/rect, :shape/path), groups (:group with :group/children), or generators (:flow-field, :contour, :scatter) that expand into shapes during rendering.

View eido.core on GitHub

Step 2: Translation

Eido's authoring grammar and the engine's grammar aren't identical, so a pure translation step maps one to the other. Colors resolve to 0..1 channels, path verbs shorten ([:move-to [x y]] becomes [:move x y]), semantic fills and gradients become tagged vectors, generators lower to generator nodes, rotations convert from radians to degrees, and paint surfaces become a paint program.

The translation is a plain function — scene data in, scene data out — so you can inspect exactly what the engine will receive before anything is rendered.

View the translator on GitHub

Step 3: Rendering

The translated scene goes to the native rendering engine. It validates the scene, expands generators into geometry, rasterizes every shape, and returns the encoded image bytes together with any diagnostics. A malformed scene comes back as structured diagnostics rather than a crash — see the Validation section.

Each call is stateless, so a render is reproducible: the same scene always produces the same bytes. That determinism is what makes seed-driven edition work dependable — an edition number always resolves to the same image.

View the render bridge on GitHub

Step 4: Output

A single scene renders to PNG; a sequence of frames renders to an animated GIF. Call render with :output to write a file, or without it to get the encoded bytes back as a value for further processing:

(eido/render scene  {:output "out.png"})
(eido/render frames {:output "anim.gif" :fps 30})

;; No :output — returns {:bytes ... :media-type ... :width ... :height ...}
(eido/render scene)

Design Decisions

Authoring core, native render shell

Eido's authoring layer is pure Clojure — every function takes data and returns data. The heavy pixel work lives in a separate native engine. That split keeps the authoring side easy to test, inspect, and compose, while the rendering side stays fast and portable.

Data all the way to the engine

Every intermediate — the scene map, the translated scene — is printable, serializable Clojure data. You can prn it, save it to a file, load it back, or write tests against it. The image is a value.

Reproducible by construction

Each render is stateless and deterministic: the same scene yields the same bytes, every time and on every platform. Reproducibility is a requirement for generative editions, not a nice-to-have.

No toolchain to install

The native engine ships compiled inside the library for each supported platform. Consumers need a JVM — no separate engine install, no build step on their machine.

Source Map

Key namespaces and what they do:

NamespaceRoleSource
eido.coreEntry point — render and scene loadingcore.clj
eido.phaneBridge to the native rendering enginephane.clj
eido.phane.translateEido grammar to engine grammartranslate.clj
eido.sceneLayout helpers, paper presets, unit conversionscene.clj
eido.gen.*Generative modules (noise, flow, circle packing, boids, etc.)gen/
eido.path.*Path stroking, distortion, warping, aestheticspath/
eido.scene3d.*3D mesh construction, cameras, shading to 2D nodesscene3d/
eido.colorColor parsing, conversion, and manipulationcolor.clj
eido.animateFrame sequencing and easinganimate.clj