Generative art playground driven by Claude Code CLI
Compose · Tweak · Explore · Fork · Export
A local-first generative art tool where you describe what you want and Claude writes the render code. No shader languages, no creative coding frameworks — just Canvas 2D and a conversation.
"spiraling triangles that pulse outward"
│
▼
┌───────────────────┐
│ Claude Sonnet │ ← writes renderCode as JS
│ system prompt │ ← knows Canvas 2D, noise, PRNG
└─────────┬─────────┘
│
▼
┌───────────────────┐
│ AlgorithmSource │ ← stored in SQLite
│ { renderCode, │
│ defaultParams } │
└─────────┬─────────┘
│
▼
┌───────────────────┐
│ new Function() │ ← compiled once, cached
│ → Canvas 2D │
└───────────────────┘
What you get:
- Describe any visual idea in plain English → get a live, tweakable sketch
- 10 built-in algorithms for instant starting points
- Sliders for every parameter, real-time preview
- Fork any sketch to branch into variations
- Explore parameter space in a grid — see 9 or 16 variations at once
- Full CLI for scripting and automation
- Everything local. SQLite. Zero config.
git clone <repo-url> && cd sketchpad
npm install
npx prisma generate && npx prisma db push
npm run devOpen localhost:3000. That's it.
Sketchpad resolves Anthropic credentials automatically — if you have Claude Code installed, it reads your OAuth token from the system keychain. No API key needed.
| Priority | Method | Setup |
|---|---|---|
| 1 | Claude Code CLI keychain | Zero config — just have Claude Code installed |
| 2 | ANTHROPIC_API_KEY env |
Standard API key in .env |
| 3 | ANTHROPIC_BASE_URL env |
Proxy / alternative endpoint |
Describe what you want. Claude writes a Canvas 2D render function, wires up parameters, picks a color palette. You get a live sketch you can tweak immediately.
npm run sketchpad -- compose "dense field of overlapping circles with watercolor transparency"Or use the web UI — the compose bar is right at the top of the New Sketch page, with Haiku-generated prompt suggestions to get you started.
Already have a composed sketch? Tell Claude what to change. It sees the current source and rewrites it.
npm run sketchpad -- refine <id> "add a radial gradient background and make circles smaller"| Algorithm | Style | Inspiration |
|---|---|---|
flow-field |
Organic flowing lines | Noise-driven vector fields |
grid-subdivide |
Recursive rectangles | Mondrian |
circle-pack |
Packed circles | Rejection sampling |
particle-flow |
Luminous particle trails | Drift simulation |
hatched-blocks |
Hachure & cross-hatch fills | Gorilla Sun's Block |
quilt-tiles |
8 tile pattern variants | Generative quilts |
watercolor-wash |
Layered transparent washes | Tyler Hobbs |
spiral-graph |
Hypotrochoid curves | Spirograph |
wave-grid |
Sine-modulated dot grid | Moire patterns |
voronoi-shatter |
Nearest-point cell fills | Voronoi diagrams |
warm · cool · mono · neon · earth · sunset · ocean · forest · pastel · brutalist · random
Each palette is 6 colors. First color is background, rest are for shapes. The random palette generates a harmonious scheme from the sketch seed using HSL color theory.
Navigate to any sketch's Explore view to see a grid of variations. Pick two numeric parameters for X and Y axes, and Sketchpad renders every combination. Click any cell to adopt those parameters.
┌─────┬─────┬─────┐
│ │ │ │ ← noiseScale varies →
├─────┼─────┼─────┤
│ │ │ │ ↑ lineCount varies
├─────┼─────┼─────┤
│ │ │ │
└─────┴─────┴─────┘
3×3 or 4×4 grid
Every sketch tracks its parent. Fork to branch into a new direction without losing the original. View the full lineage chain in the workspace breadcrumb.
npm run sketchpad -- fork <id> --name "sunset variation"
npm run sketchpad -- history <id>All commands output JSON. Pretty-printed on TTY, compact when piped.
npm run sketchpad -- <command> [options]| Command | Description |
|---|---|
new --algo <name> |
Create sketch from built-in algorithm |
compose "prompt" |
AI-generate a custom algorithm |
refine <id> "instruction" |
AI-refine a composed sketch |
critique <id> |
AI critique with suggestions |
list [--limit N] |
List recent sketches |
show <id> |
Full sketch detail |
tweak <id> key=value |
Update parameters |
seed <id> --random |
Randomize seed |
fork <id> |
Branch from existing sketch |
explore <id> --vary p1,p2 |
Parameter space grid |
history <id> |
Show fork lineage |
algorithms |
List all algorithms + params |
palettes |
List color palettes |
# Compose something from scratch
npm run sketchpad -- compose "crystalline void with floating shards"
# See what we got
npm run sketchpad -- show <id>
# Tweak parameters
npm run sketchpad -- tweak <id> shardCount=200 opacity=0.6
# Not quite right — refine with AI
npm run sketchpad -- refine <id> "make the shards more angular, add depth with size variation"
# Happy — fork for a color variant
npm run sketchpad -- fork <id> --name "crystalline void — warm"
npm run sketchpad -- tweak <new-id> palette=sunset
# Explore the parameter space
npm run sketchpad -- explore <id> --vary shardCount,opacity --grid 4
# Export from the web UI
open http://localhost:3000/sketch/<id>When you compose, Claude receives a system prompt that teaches it:
- The
render(ctx, params, seed, width, height)function signature - Available utilities:
createSeededRandom(seed),createNoise2D(seed),getPalette(name, seed) - Canvas 2D API patterns for generative art
- Compositional techniques (layering, noise fields, particle systems)
- Strict JSON output format with
renderCodeas executable JS
The generated code is stored in AlgorithmSource, compiled once via new Function(), cached, and executed on every render with the engine utilities injected as parameters. Same seed always produces the same output.
┌──────────────────────────────────────────────────────┐
│ Engine Utilities │
├──────────────┬──────────────┬────────────────────────┤
│ SeededRandom │ Noise2D │ Palettes │
│ .next() │ simplex 2D │ 11 palettes + random │
│ .range() │ returns │ 6 colors each │
│ .pick() │ -1..1 │ HSL generation for │
│ .shuffle() │ │ random palettes │
└──────────────┴──────────────┴────────────────────────┘
↓ injected into every render call
┌──────────────────────────────────────────────────────┐
│ renderCode(ctx, params, seed, w, h, │
│ createSeededRandom, createNoise2D, │
│ getPalette) │
└──────────────────────────────────────────────────────┘
app/
page.tsx # Dashboard — sketch grid
sketch/new/ # Algorithm picker + AI compose
sketch/[id]/ # Workspace — canvas + params + refine
sketch/[id]/explore/ # Parameter space grid
api/
sketches/ # CRUD + fork
ai/ # compose, refine, critique, describe, suggest
sources/ # AlgorithmSource lookup
lib/
engine/
algorithms/ # 10 built-in render functions
dynamic-algorithm.ts # JSON → compiled Function (cached)
renderer.ts # renderAny / renderSketch dispatch
registry.ts # Built-in algorithm lookup
palettes.ts # 11 color palettes + random generation
random.ts # Seeded PRNG (mulberry32)
noise.ts # Simplex noise 2D
types.ts # AlgorithmDef, ParamDef, ParamValue
ai/
system-prompt.ts # Claude system prompts for compose/refine
compose-service.ts # AI API calls + JSON parsing
ai-client.ts # Anthropic SDK wrapper
claude-cli-auth.ts # OAuth token from Claude Code keychain
db.ts # Prisma client
cli/
sketchpad.ts # 13 commands, JSON output
prisma/
schema.prisma # Sketch, Tag, AlgorithmSource
dev.db # SQLite (auto-created)
SQLite via Prisma. Three models:
- Sketch — name, algorithm, params (JSON), seed, parentId (fork lineage)
- Tag — categorization (many-to-one with Sketch)
- AlgorithmSource — stores AI-generated algorithm definitions (name, source JSON, version)
Built-in: getAlgorithm(name) → algo.render(ctx, mergedParams, seed, w, h)
Composed: cache.get(sourceJson) || (JSON.parse → new Function → cache) → algo.render(...)
Dispatch: renderAny(canvas, name, params, seed, sourceJson?) handles both paths
Compiled algorithms are cached by source JSON — no recompilation on re-render or parameter changes.
| Layer | Technology | Role |
|---|---|---|
| Framework | Next.js 16 (App Router) | Server components, API routes |
| Language | TypeScript 5.8 | Type safety throughout |
| Database | Prisma 7 + SQLite | Local-first, zero setup |
| Rendering | Canvas 2D | Pure, no external graphics libs |
| Parameters | Tweakpane 4 | Real-time slider UI |
| AI | Claude Sonnet 4 | Algorithm generation + refinement |
| AI (fast) | Claude Haiku 4.5 | Prompt suggestions |
| Styling | Tailwind CSS 4 | Utility-first dark theme |
| Icons | Lucide React | Minimal icon set |
npm run dev # Dev server with Turbopack
npm run build # Production build
npm run sketchpad -- ... # CLI commands
npx prisma studio # Browse database
npx prisma db push # Apply schema changes- Create
lib/engine/algorithms/your-algo.tsexporting anAlgorithmDef - Register in
lib/engine/registry.ts - Done — it appears in the web UI picker and CLI
interface AlgorithmDef {
name: string
label: string
description: string
defaultParams: Record<string, ParamDef>
render: (ctx, params, seed, width, height) => void
}Built for playing with shapes, noise, and color.
