Beta — The core API is stabilizing. Breaking changes may still occur between releases, but the fundamentals are in place.

Eido

An end-to-end Clojure toolkit for generative artists — from REPL sketch to screen, print, and plotter.

From Greek eido — "I see." A tool for making art, not charts — see Scope.

Images are values
A scene is a plain data structure — printable, serializable, diffable. Nothing opaque.
One function
render takes a scene (or a sequence of scenes) and produces output. That's the entire API.
Description, not instruction
You declare what the image contains — Eido decides how to draw it.
Animations are sequences
60 frames = 60 scenes in a list. No timeline, no keyframes, no mutable state.
3D sculpting pipeline
Composable mesh operations — deform, extrude, subdivide, mirror — all pure data, chainable via ->.
2D↔3D bridge
Same fields, palettes, and programs work across both domains. UV-mapped procedural textures on 3D meshes.
Particle simulation
Physics-based effects — fire, snow, sparks — configured as data with deterministic results.
Typography as paths
Text converted to vector paths — compatible with gradients, transforms, and 3D extrusion.
Bring your own workflow
Every function takes data and returns data. No framework, no state management.
Zero dependencies
Just the language and the standard library. Nothing to install, nothing to break.

How it works

In Eido, an image is just a description — a plain data structure that says what the image contains. Here's a red circle on a light background:

{:image/size [400 400]                       ;; 400x400 pixels
 :image/background [:color/name "linen"]      ;; warm off-white
 :image/nodes
 [{:node/type     :shape/circle
   :circle/center [200 200]                  ;; center of the canvas
   :circle/radius 120
   :style/fill    [:color/name "crimson"]}]}  ;; red fill

That's it — no drawing commands, no canvas API, no mutable state. You describe what you see, and Eido renders it. To produce an image file:

(eido/render scene {:output "circle.png"})

Want animation? Return a different scene for each frame. Here's a circle that grows and shifts color over 60 frames:

(def frames
  (anim/frames 60
    (fn [t]                             ;; t goes from 0.0 to 1.0
      {:image/size [400 400]
       :image/background [:color/rgb 30 30 40]
       :image/nodes
       [{:node/type     :shape/circle
         :circle/center [200 200]
         :circle/radius (* 150 t)       ;; grows over time
         :style/fill [:color/hsl        ;; hue shifts through
                      (* 360 t)         ;;   the rainbow
                      0.8 0.5]}]})))

(eido/render frames {:output "grow.gif" :fps 30})

Convenience helpers let you write the same thing more concisely:

;; Shorthand — same circle, fewer keystrokes
(require '[eido.scene :as scene])
(require '[eido.color :as color])

(scene/circle-node [200 200] 120 (color/hsl 0 0.8 0.5))

Low-level control when you need it, high-level convenience when you don't. Every example in the gallery works this way — pure data in, image out. See the Manual to get started.

Getting Started

New to Clojure? Follow the Calva Getting Started guide — it walks you through installing VS Code, Clojure, and the Calva extension, which lets you evaluate code directly from your editor.

1. Add Eido to your project

Create a deps.edn file with Eido as a dependency:

;; deps.edn
{:deps
 {io.github.leifericf/eido
  {:git/tag "v1.0.0-beta7" :git/sha "a94504c"}}}

2. Render your first image

Start a REPL via Calva ("Jack-in"), then evaluate:

(require '[eido.core :as eido])

(eido/render
  {:image/size [400 400]
   :image/background [:color/name "linen"]
   :image/nodes
   [{:node/type     :shape/circle
     :circle/center [200 200]
     :circle/radius 120
     :style/fill    [:color/name "coral"]}]}
  {:output "my-first-image.png"})
;; => "my-first-image.png"

That's it — my-first-image.png is now on disk. Change the color, re-evaluate, see the new image. This interactive loop is how generative artists work with Eido.