Etcha turns a plain-language prompt into a durable Etch A Sketch-style tape you can inspect, edit, validate, replay in a floating macOS window, and rotate through a screen saver.
Open this repo in Claude Code or Codex and say:
Use `$etcha` and draw me based on how I prompt AI in my recent Claude Code and Codex logs from the last week. Look at my last 8 transcripts, turn that into a symbolic self-portrait, keep it inside the frame, and replay it slowly.
That is the default agent-first demo for this repo. The manual build-and-queue path is still documented below.
The Problem: Most prompt-to-art tools stop at a raster image or an opaque UI state. You get a result, but not a durable artifact you can diff, repair, validate, or replay later.
The Solution: Etcha converts prompts into plain-text motion tapes, stores each job in a visible workspace on disk, validates every step against a fixed board, and reuses the same playback logic across the agent window and the screen saver.
| Feature | What It Does |
|---|---|
| Plain-text tapes | Every completed drawing lives as tape.etcha, not as an opaque blob. |
| Deterministic playback | The floating app and the screen saver both consume the same compiled tape model. |
| Visible workspace | Prompts, manifests, logs, candidate output, canonical tapes, and validation reports all live under ~/Etcha/ by default. |
| Manual rescue path | If generation is close but wrong, edit the tape by hand and run make validate. |
| Board-aware validation | Motion must stay inside a 280 x 192 board for every intermediate step. |
| Ambient display surface | Completed jobs can be replayed in a mini window or rotated as a macOS screen saver. |
# 1. Build the CLI and agent
make build
# 2. Launch the floating player / job runner
open -n .derived/Build/Products/Debug/EtchaAgent.app
# 3. Queue a drawing prompt
make queue PROMPT="draw a tree on a hill"
# 4. Watch jobs appear in the workspace
make list
# 5. Inspect a finished job and reveal it in Finder
make show JOB=<job_id>
# 6. Replay the same tape more slowly
make play JOB=<job_id> SPEED=slow
# 7. Open the workspace if you want to hand-edit the tape
make open-workspace
# 8. Revalidate your manual edit
make validate JOB=<job_id>- You queue a prompt with
etcha "<prompt>"ormake queue PROMPT="...". - The CLI creates a job folder under
~/Etcha/jobs/<job_id>/and writesprompt.txt. EtchaAgent.appclaims the oldest queued job, builds a Codex instruction file, and runscodex exec.- Codex writes
tape.candidate.etcha. - Etcha validates the candidate tape against the board rules.
- If valid, the candidate is promoted to
tape.etcha. - The same canonical tape can then be replayed in the mini window or the screen saver.
The image is not the primary artifact. The primary artifact is the tape because the tape is what you can replay, diff, archive, and repair.
Etcha is not a freeform vector editor. It deliberately keeps the board size, cursor origin, and movement grammar tight so the output still feels like an Etch A Sketch system.
Jobs live in normal folders with normal files. If generation fails, you can inspect the prompt, stderr log, candidate tape, and validation report without a bespoke debugging UI.
The floating window and the screen saver are only useful if the same tape means the same drawing on both surfaces.
Codex is the default authoring path, not a sacred one. Manual tape editing is a first-class recovery workflow.
| Capability | Etcha | Manual tape authoring | Generic image generator | One-off macOS scripts |
|---|---|---|---|---|
| Prompt to drawing flow | ✅ Built in | ❌ None | ✅ Usually image-only | |
| Human-editable artifact | ✅ tape.etcha |
✅ tape.etcha |
❌ Usually opaque | |
| Board-bounded validation | ✅ Yes | ✅ Via Etcha validator | ❌ No | |
| Floating ambient player | ✅ Yes | ❌ Not by itself | ❌ No | |
| Screen saver rotation | ✅ Yes | ❌ Not by itself | ❌ No | |
| Workspace logs and manifests | ✅ First-class | ❌ Rarely | ||
| Setup cost today | ✅ Minimal | ✅ Minimal | ❌ High |
When to use Etcha:
- You want prompt-driven drawings that remain editable after generation.
- You want a durable playback artifact rather than a one-shot image.
- You want a small local macOS display surface for replaying finished work.
When Etcha is not ideal:
- You need a packaged installer, signed release, or Homebrew formula.
- You need arbitrary vector art or image export formats.
- You need collaboration, sync, or cloud-hosted generation.
- macOS 15 or newer
- Xcode with Swift 6 toolchain support
codexavailable on yourPATHif you want prompt-to-tape generationxcodegenonly if you plan to regenerateEtcha.xcodeprojfromproject.yml
If you only want to inspect or replay existing tapes, Codex is not required.
Etcha is source-built today. There is no curl installer, Homebrew formula, or signed app release in this repo yet.
make buildThis produces:
.derived/Build/Products/Debug/etcha.derived/Build/Products/Debug/EtchaAgent.app
xcodebuild -project Etcha.xcodeproj -scheme EtchaAgent -destination 'platform=macOS' -derivedDataPath .derived build
xcodebuild -project Etcha.xcodeproj -scheme etcha -destination 'platform=macOS' -derivedDataPath .derived build
xcodebuild -project Etcha.xcodeproj -scheme EtchaSaver -destination 'platform=macOS' -derivedDataPath .derived buildopen Etcha.xcodeprojBuild and run the EtchaAgent, etcha, EtchaSaver, or EtchaTests schemes directly from Xcode.
If you change project.yml, regenerate the Xcode project first:
xcodegen generateOpen this repo in Claude Code or Codex and say:
Use `$etcha` and draw me based on how I prompt AI in my recent Claude Code and Codex logs from the last week. Look at my last 8 transcripts, infer a symbolic self-portrait, keep it inside the frame, and replay it slowly.
Change last week or last 8 transcripts if you want a different window.
make build
open -n .derived/Build/Products/Debug/EtchaAgent.app
make queue PROMPT="draw a spiral"
make listmake play JOB=<job_id> SPEED=normalmake show JOB=<job_id>
$EDITOR ~/Etcha/jobs/<job_id>/tape.etcha
make validate JOB=<job_id>
make play JOB=<job_id> SPEED=1.5make build-saver
make install-saverThen open macOS Screen Saver settings and select EtchaSaver. The saver cycles through completed jobs. If there are no completed jobs, its preview view falls back to a built-in demo tape.
Etcha tapes are line-oriented text files. Valid directives are:
| Directive | Meaning | Example |
|---|---|---|
title <text> |
Optional display title | title Box demo |
clear |
Clears the board and resets the cursor to center | clear |
speed <multiplier> |
Playback speed from 0.25 to 4.0 |
speed 1.5 |
pause <ticks> |
Waits without drawing | pause 20 |
w/a/s/d moves |
Moves cursor one axis or a diagonal per tick | d12, wa30, sd8 |
Movement rules:
- The first non-comment command must be
clear. - Movement tokens must be one or two letters from
w,a,s,dfollowed by a positive integer. - Opposite pairs like
ws10orad10are invalid. - Duplicate letters like
ww10are invalid. - Every intermediate movement step must remain in bounds.
- The board starts at
x=140,y=96on a280 x 192board.
Example tape:
title Square demo
clear
speed 1.8
d18
s18
a36
w36
d18
pause 20
By default, Etcha uses ~/Etcha. You can override that with ETCHA_WORKSPACE.
~/Etcha/
├── cache/
│ └── playback-request.json
└── jobs/
└── 20260329-104500-draw-a-cat/
├── prompt.txt
├── manifest.json
├── codex.prompt.txt
├── codex.last.txt
├── codex.stderr.log
├── tape.candidate.etcha
├── tape.etcha
└── validation.json
Important files:
prompt.txt: The original queued prompt.manifest.json: Job status, timestamps, failure codes, playback metadata, and file paths.codex.prompt.txt: The exact prompt Etcha sends tocodex exec.tape.candidate.etcha: Raw model output before promotion.tape.etcha: Canonical replayable tape after validation succeeds.validation.json: Validation result and parse / bounds issues.
Etcha has two main user surfaces:
- the
etchaCLI - the repo
Makefile, which wraps the common local development flows
Queues a new job and tries to ensure the agent is running.
.derived/Build/Products/Debug/etcha "draw a boat"
make queue PROMPT="draw a boat"Lists all jobs in the workspace.
.derived/Build/Products/Debug/etcha list
make listPrints job metadata and reveals the job folder in Finder.
.derived/Build/Products/Debug/etcha show 20260329-104500-draw-a-boat
make show JOB=20260329-104500-draw-a-boatOpens the workspace root in Finder.
.derived/Build/Products/Debug/etcha open
make open-workspaceRevalidates tape.etcha after manual editing.
.derived/Build/Products/Debug/etcha validate 20260329-104500-draw-a-boat
make validate JOB=20260329-104500-draw-a-boatQueues playback in the agent window.
.derived/Build/Products/Debug/etcha play 20260329-104500-draw-a-boat --speed slow
.derived/Build/Products/Debug/etcha play 20260329-104500-draw-a-boat --speed 1.5
make play JOB=20260329-104500-draw-a-boat SPEED=fast| Target | What It Does | Example |
|---|---|---|
make build |
Builds the CLI and agent | make build |
make build-saver |
Builds EtchaSaver.saver |
make build-saver |
make test |
Runs the test bundle | make test |
make run-agent |
Runs the agent in the foreground | make run-agent |
make run-agent-bg |
Runs the agent in the background and logs to .derived/EtchaAgent.log |
make run-agent-bg |
make queue |
Queues a new prompt | make queue PROMPT="draw a star" |
make list |
Lists jobs | make list |
make show |
Shows one job | make show JOB=<job_id> |
make play |
Queues playback | make play JOB=<job_id> SPEED=slow |
make validate |
Revalidates a tape | make validate JOB=<job_id> |
make open-workspace |
Opens the workspace in Finder | make open-workspace |
make install-saver |
Copies the screen saver bundle into ~/Library/Screen Savers |
make install-saver |
| Preset | Multiplier |
|---|---|
slow |
0.5 |
normal |
1.0 |
fast |
2.0 |
Literal multipliers are also supported:
make play JOB=<job_id> SPEED=1.5Etcha does not use a config file yet. Configuration is currently driven by environment variables and Make overrides.
| Variable | Meaning | Default |
|---|---|---|
ETCHA_WORKSPACE |
Workspace root used by the CLI, agent, and saver | ~/Etcha |
ETCHA_AGENT_APP_PATH |
Explicit path the CLI should use when trying to auto-launch the agent | Auto-discovered from build products and standard app locations |
| Variable | Meaning | Default |
|---|---|---|
WORKSPACE |
Workspace root passed into CLI / agent commands | $(HOME)/Etcha |
DERIVED_DATA |
Xcode build output directory | .derived |
CONFIGURATION |
Xcode build configuration | Debug |
SPEED |
Playback speed preset or multiplier for make play |
none |
Examples:
WORKSPACE=/tmp/etcha make run-agent
WORKSPACE=/tmp/etcha make queue PROMPT="draw a kite"
ETCHA_AGENT_APP_PATH="$PWD/.derived/Build/Products/Debug/EtchaAgent.app" \
.derived/Build/Products/Debug/etcha "draw a wave"┌────────────────────────────────────────────────────────────────────┐
│ User Input │
│ prompt text, manual tape edits, play requests │
└────────────────────────────────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────────────┐
│ CLI │
│ etcha <prompt> / list / show / open / validate / play │
└────────────────────────────────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────────────┐
│ WorkspaceManager │
│ ~/Etcha/jobs/* + manifest.json + validation.json + cache │
└────────────────────────────────────────────────────────────────────┘
│
┌─────────────┴─────────────┐
▼ ▼
┌──────────────────────────┐ ┌─────────────────────────────────┐
│ EtchaAgent.app │ │ Manual Editing │
│ claims queued jobs │ │ edit tape.etcha │
│ runs codex exec │ │ run validate afterward │
└──────────────────────────┘ └─────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────────────┐
│ Tape Validation │
│ parse directives → check movement grammar → enforce board bounds │
└────────────────────────────────────────────────────────────────────┘
│
┌─────────────┴─────────────┐
▼ ▼
┌──────────────────────────┐ ┌─────────────────────────────────┐
│ tape.etcha │ │ validation.json │
│ canonical replay source │ │ errors, warnings, timing │
└──────────────────────────┘ └─────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────────────┐
│ Playback Surfaces │
│ EtchaAgent mini window + EtchaSaver screen saver │
└────────────────────────────────────────────────────────────────────┘
The CLI can queue the job, but automatic generation will not start until the agent exists in a discoverable location.
make build
open -n .derived/Build/Products/Debug/EtchaAgent.appIf your app bundle lives elsewhere:
ETCHA_AGENT_APP_PATH="/absolute/path/to/EtchaAgent.app" \
.derived/Build/Products/Debug/etcha "draw a moon"Codex generation took longer than the current hard timeout.
make show JOB=<job_id>
open ~/Etcha/jobs/<job_id>/codex.stderr.logThen retry with a simpler prompt or author the tape manually.
codex exec did not produce a usable candidate file.
which codex
make show JOB=<job_id>
open ~/Etcha/jobs/<job_id>/codex.stderr.logMake sure codex is installed and runnable in the environment where EtchaAgent.app launches.
The tape contains an unsupported directive or malformed movement token.
Common causes:
- Missing initial
clear - Invalid token like
north10 - Duplicate direction like
ww12 - Opposite directions like
ws20
After editing, re-run:
make validate JOB=<job_id>At least one movement would leave the 280 x 192 board.
start x=140, y=96
valid x range: 0...279
valid y range: 0...191
Shorten the movement count or split the path into smaller in-bounds segments, then validate again.
The saver rotates only completed jobs. Failed, queued, and invalid jobs are ignored.
make list
make validate JOB=<job_id>Make sure at least one job is in completed status with a valid tape.etcha.
- macOS-only
- Source-build only
- No installer, updater, or signed release flow yet
- No export formats beyond the tape itself
- No collaborative or cloud-hosted workspace
- Prompt generation depends on a working local
codexCLI - Tape language is intentionally constrained and not suitable for arbitrary vector drawing
Only for prompt-to-tape generation. Replaying existing tapes and manually authored tapes does not require Codex.
By default in ~/Etcha, with per-job folders under ~/Etcha/jobs/.
Yes. That is an intended workflow. Edit tape.etcha, then run make validate.
tape.candidate.etcha is raw model output. tape.etcha is the canonical tape copied into place only after validation succeeds.
No. The screen saver cycles only through completed jobs with valid canonical tapes.
Yes. The built app bundle is at .derived/Build/Products/Debug/EtchaAgent.app, and the CLI binary is at .derived/Build/Products/Debug/etcha.
This repo currently does not include a LICENSE file. Until one is added, do not assume an open-source license has been granted.
About Contributions: Please don't take this the wrong way, but I do not accept outside contributions for any of my projects. I simply don't have the mental bandwidth to review anything, and it's my name on the thing, so I'm responsible for any problems it causes; thus, the risk-reward is highly asymmetric from my perspective. I'd also have to worry about other "stakeholders," which seems unwise for tools I mostly make for myself for free. Feel free to submit issues, and even PRs if you want to illustrate a proposed fix, but know I won't merge them directly. Instead, I'll have Claude or Codex review submissions via
ghand independently decide whether and how to address them. Bug reports in particular are welcome. Sorry if this offends, but I want to avoid wasted time and hurt feelings. I understand this isn't in sync with the prevailing open-source ethos that seeks community contributions, but it's the only way I can move at this velocity and keep my sanity.
make testThis runs the current EtchaTests bundle, which covers queued job defaults, successful candidate promotion, validation failure handling, and out-of-bounds rejection.
No license file is currently present in this repository.