Diorama guides

This page covers Diorama SVG. For textured miniature stories and four live finishes, read the Diorama Worlds guide; for dithered pixel-art miniatures, the Diorama Pixel guide.

Diorama SVG builds lightweight interactive figures in one offline HTML file: a dependency-free renderer, six themes, eighteen examples and browser checks. Worlds and Pixel use separate renderers and their own install commands.

Installation

npx skills add educlopez/diorama --skill diorama -g

Selects only Diorama SVG. The CLI lets you choose target agents. Add -a claude-code to target Claude Code alone. Node.js 20 or later runs the scripts; Chrome or Chromium runs the look (Playwright's browser is found, or playwright-core is installed once).

git clone https://github.com/educlopez/diorama.git ~/.diorama && mkdir -p ~/.claude/skills && ln -s ~/.diorama/skills/diorama ~/.claude/skills/diorama

The same, with git: the clone stays in ~/.diorama, git pull there updates it. Nothing is needed at runtime: a finished page draws itself as SVG and makes no network request.

Prompts

Type /diorama and describe the place. Say where it will live (a hero, a card, a small figure), name the place or the product, and give a colour if you have one. The agent offers two or three concepts, builds the one you pick, runs the validator and the look, and hands you diorama-<name>.html.

/diorama a hero for our logistics landing page: a yard with trucks docking
/diorama an empty state for the inbox, small and quiet, line-drawing style
/diorama a card for the docs: jobs wait in line, one runner takes them
/diorama a harbour at dusk for the top of our README, brand colour #5b4ae0

Sizes

The size sets the stage, the budget and the tempo.

SizeLivesStageMovesLines
herothe top of a landing page or a README3:2, full widththree or four loops with different periods, one pointer answer≤ 420
cardbeside text in docs or a post4:3, about 500pxone process, one pointer answer≤ 240
minia small spot: an empty state, a feature list, a sidebar, a 404, an icon-sized hero1:1, 160 to 240pxone quiet ambient motion≤ 150

Themes

A theme is a set of fields the kernel applies to every primitive; a figure never branches on it, so the same file draws five ways. Pick it from where the figure will live.

ThemeReads asKeepsUse it for
toypastel blocks, one warm sun, a cool fill, soft shadowsevery colourproduct pages, READMEs, anything warm
softthe same on cream, gentler light, desaturated accentsevery colour, shifted warmeditorial pages, paper backgrounds
monotints of ink on whiteone accentdocs, product UIs with their own accent, text-heavy pages
linea hairline drawing: white fills, ink edges, silhouettes, no shadows; by hand with wobbleone accent, as a strokeminimal or technical pages, small quiet figures
nightdusk: dark base, cool sky, warm lamps and windowsevery colour, darkerdark pages, hero sections on black
printflat tones per face and ink lines, no thrown shadows: a printed illustrationevery coloureditorial pages, docs that want a drawing
Overrides, in the figure or on mount
theme: { base: 'mono', accent: '#f2c230', shadows: false }
theme: { base: 'toy', palette: { coral: '#5b4ae0', grass: '#d8ead0' } }
theme: { base: 'line', round: true, strokeWidth: 1.4 }
theme: { base: 'night', glow: 3, lights: { sun: ['#ffd6a0', .3] } }
FieldWhat it does
bgthe stage colour, or 'transparent'
paletteoverrides of any palette entry: base, ground, grass, water, paper, ink, coral, mustard, teal, plum, …
accentthe subject's colour in every theme, and the one mono and line keep
lights{ hemi: [sky, ground, i], sun: [c, i], fill: [c, i] | null, ambient: [c, i] }
shadows, shadowtrue or false, and how dark the thrown shadows are (0..1)
tones[top, lit side, dark side]: three flat tones for every box face instead of the lights (print)
tone, tint{ paper, ink } makes any theme monochrome; tint 0..1 lets colour back in
edges, outlineink lines on flat solids, silhouettes on round ones
stroke, strokeWidththe line colour and width in css px
roundevery box becomes a rounded solid
glowa multiplier on every emissive
halo0..1, the soft shadow the base throws on the page
wobblecss px of hand tremor on the ink: 1.2 a pen, 2.5 a felt tip

Minis

A mini is a small figure, 160 to 240px square: one object and one quiet motion. Use it wherever a page needs a small picture: an empty state, a feature in a list, a settings section, a 404, a sidebar. Its still frame is the picture and the ambient motion is the bonus. The canvas is transparent, so whatever sits behind it shows through. The words around it, a heading, a line, a button, are yours to write in your own UI.

Mailboxmini · toyrest

The figure file

One Diorama.figure call: a declaration, a build that makes the scene once with the toolkit and returns the parts that move, and a tick that poses them from ctx.t. No timers, no new THREE.*, no words inside the scene.

mailbox.js, abridged
Diorama.figure({
  name: 'mailbox',
  size: 'mini',                          // hero | card | mini
  theme: 'toy',                          // or { base: 'line', accent: '#cf6a4c' }
  means: 'An open letterbox with nothing inside. The flag lifts now and then.',
  title: 'Mailbox',
  still: 1,                              // the second reduced motion shows
  build: function (o) {
    var D = o.D, pal = o.pal;
    D.slab(3, 3, pal.grass, 0, 0, 0, null, { h: .5, r: .45, side: pal.base });
    var post = D.group(.15, 0, -.1);
    D.cyl(.07, .09, 1.1, pal.wood, 0, .55, 0, post, 8);
    var body = D.group(0, 1.15, 0, post);
    D.box(.95, .5, .6, pal.coral, 0, .25, 0, body);
    var flag = D.group(.1, .5, .32, body);
    D.box(.26, .16, .03, pal.mustard, .1, .02, 0, flag);
    D.props.tree(1, -.8, null, { r: .42 });
    return { flag: flag };
  },
  tick: function (ctx) {
    var D = ctx.D, t = ctx.t, check = D.smooth(D.pulse(t, 7, .36));
    ctx.parts.flag.rotation.z = D.lerp(.2, 1.45, check);
  }
});

The toolkit: D.box, D.rbox, D.cyl, D.cone, D.ball, D.dome, D.wedge, D.slab, D.torus, D.wheel, D.label; the props tree, bush, rock, cloud, person, lamp, crate, pallet, cone, flag; for motion phase, pulse, wave, the eases, spring, apart, path, place, spin, stagger. The index at the top of kit/diorama.js lists them all.

Scripts

node ~/.claude/skills/diorama/scripts/build.mjs mailbox.js

Writes diorama-mailbox.html: the kernel, the figure and the bench page, one file that draws itself as SVG with nothing to load.

node ~/.claude/skills/diorama/scripts/validate.mjs diorama-mailbox.html

Ten checks: the declaration, the length, the clock, self-containment, the kernel, the words, the colours, the base, the pointer and the drawing (no renderer, no three.js in the page).

node ~/.claude/skills/diorama/scripts/look.mjs mailbox.js --theme line

Opens the page in headless Chromium and writes one sheet: the still frame, three moments, the pointer, the tilt, a press, the thumbnail and the page, plus the console, the fill, the motion, the answer and the press as numbers.

Embed

The built page can be served as is or framed. For a product, keep the kernel and the figure and drop the bench. The kernel sizes the canvas to the element’s width, watches resizes, sleeps offscreen and honours prefers-reduced-motion with one still frame. Remount to change the theme.

Any page
<div id="hero" style="width:100%"></div>
<script src="/diorama/kit/diorama.js"></script>
<script src="/diorama/island.js"></script>
<script>
  var fig = Diorama.figures[Diorama.figures.length - 1];
  var ctx = Diorama.mount(fig, { el: document.getElementById('hero'), theme: 'night' });
</script>
React or Next.js
"use client";
import { useEffect, useRef } from "react";
import Diorama from "./diorama.js";
import "./island.js";                   // registers the figure

export function Hero({ theme = "toy" }) {
  const el = useRef<HTMLDivElement>(null);
  useEffect(() => {
    const fig = Diorama.figures[Diorama.figures.length - 1];
    const ctx = Diorama.mount(fig, { el: el.current!, theme });
    return () => ctx.destroy();
  }, [theme]);
  return <div ref={el} style={{ width: "100%", aspectRatio: "3 / 2" }} aria-hidden />;
}

Craft rules

Ten rules separate a diorama from a pile of cubes: the world stands on a rounded base with its zones inside the corners; the camera is true isometric, subject front-left; the palette is pale with two or three accents and one named accent; materials are matte blocks, no textures; every object is a few primitives with the two or three features that give it away; one sun and soft shadows under everything; all motion comes from the clock in tick; vehicles ride paths on a schedule with stops that have a cause; a few loops with different tempos and one rare thing; and no words inside the scene. The long form, with recipes, is in references/craft.md.

What is in the skill

FileWhat it is
SKILL.mdthe workflow: size and concept, build, check, hand over, adjust
kit/diorama.jsthe kernel, no dependency: scene graph, isometric camera, the SVG painter (flat tones, thrown shadows, halo, ink), themes, primitives, props, paths, springs, the pointer, the loop
bench.htmlthe page a figure is built into; a theme switcher, accent swatches, a live read-out and a still-frame toggle
scripts/build.mjswrites diorama-<name>.html: the bench, the kernel and the figure, nothing to load
scripts/validate.mjsten checks: declaration, length, clock, self-contained, kernel, words, colours, base, pointer, drawing
scripts/look.mjsa sheet of nine pictures and the numbers, in headless Chromium; --theme for another theme, --press for what to tap
references/concepts, the ten craft rules and the themes, motion, the look, React
examples/eighteen figures across sizes and themes, the format to write
npx skills add educlopez/diorama --skill diorama -g