Skip to content

build000r/etcha

Repository files navigation

Etcha

Etcha icon

Platform Swift Runtime Generator Status

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.

Quick Start

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.

TL;DR

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.

Why Use Etcha?

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.

Manual Example

# 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>

How It Works

  1. You queue a prompt with etcha "<prompt>" or make queue PROMPT="...".
  2. The CLI creates a job folder under ~/Etcha/jobs/<job_id>/ and writes prompt.txt.
  3. EtchaAgent.app claims the oldest queued job, builds a Codex instruction file, and runs codex exec.
  4. Codex writes tape.candidate.etcha.
  5. Etcha validates the candidate tape against the board rules.
  6. If valid, the candidate is promoted to tape.etcha.
  7. The same canonical tape can then be replayed in the mini window or the screen saver.

Design Philosophy

1. The tape is the product

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.

2. Constrained motion is the point

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.

3. Local and inspectable beats magical

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.

4. Playback semantics must match everywhere

The floating window and the screen saver are only useful if the same tape means the same drawing on both surfaces.

5. Prompt generation is optional

Codex is the default authoring path, not a sacred one. Manual tape editing is a first-class recovery workflow.

How Etcha Compares

Capability Etcha Manual tape authoring Generic image generator One-off macOS scripts
Prompt to drawing flow ✅ Built in ❌ None ✅ Usually image-only ⚠️ You build it
Human-editable artifact tape.etcha tape.etcha ❌ Usually opaque ⚠️ Depends on your format
Board-bounded validation ✅ Yes ✅ Via Etcha validator ❌ No ⚠️ Only if you write it
Floating ambient player ✅ Yes ❌ Not by itself ❌ No ⚠️ You build it
Screen saver rotation ✅ Yes ❌ Not by itself ❌ No ⚠️ You build it
Workspace logs and manifests ✅ First-class ⚠️ Manual ❌ Rarely ⚠️ Ad hoc
Setup cost today ⚠️ Source build ✅ 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.

Prerequisites

  • macOS 15 or newer
  • Xcode with Swift 6 toolchain support
  • codex available on your PATH if you want prompt-to-tape generation
  • xcodegen only if you plan to regenerate Etcha.xcodeproj from project.yml

If you only want to inspect or replay existing tapes, Codex is not required.

Installation

Etcha is source-built today. There is no curl installer, Homebrew formula, or signed app release in this repo yet.

Option 1: Build From the Checkout

make build

This produces:

  • .derived/Build/Products/Debug/etcha
  • .derived/Build/Products/Debug/EtchaAgent.app

Option 2: Raw xcodebuild

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 build

Option 3: Open in Xcode

open Etcha.xcodeproj

Build and run the EtchaAgent, etcha, EtchaSaver, or EtchaTests schemes directly from Xcode.

Option 4: Regenerate the Project

If you change project.yml, regenerate the Xcode project first:

xcodegen generate

Agent Quick Start

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, 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.

Manual Quick Start

Queue your first drawing manually

make build
open -n .derived/Build/Products/Debug/EtchaAgent.app
make queue PROMPT="draw a spiral"
make list

Replay a finished drawing

make play JOB=<job_id> SPEED=normal

Edit a tape manually

make show JOB=<job_id>
$EDITOR ~/Etcha/jobs/<job_id>/tape.etcha
make validate JOB=<job_id>
make play JOB=<job_id> SPEED=1.5

Use the screen saver

make build-saver
make install-saver

Then 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.

Tape Format

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, d followed by a positive integer.
  • Opposite pairs like ws10 or ad10 are invalid.
  • Duplicate letters like ww10 are invalid.
  • Every intermediate movement step must remain in bounds.
  • The board starts at x=140, y=96 on a 280 x 192 board.

Example tape:

title Square demo
clear
speed 1.8
d18
s18
a36
w36
d18
pause 20

Workspace Layout

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 to codex 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.

Commands

Etcha has two main user surfaces:

  • the etcha CLI
  • the repo Makefile, which wraps the common local development flows

CLI Commands

etcha <prompt>

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"

etcha list

Lists all jobs in the workspace.

.derived/Build/Products/Debug/etcha list
make list

etcha show <job_id_or_path>

Prints 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-boat

etcha open

Opens the workspace root in Finder.

.derived/Build/Products/Debug/etcha open
make open-workspace

etcha validate <job_id_or_path>

Revalidates tape.etcha after manual editing.

.derived/Build/Products/Debug/etcha validate 20260329-104500-draw-a-boat
make validate JOB=20260329-104500-draw-a-boat

etcha play <job_id_or_path> [--speed <preset_or_multiplier>]

Queues 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

Make Targets

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

Speed Presets

Preset Multiplier
slow 0.5
normal 1.0
fast 2.0

Literal multipliers are also supported:

make play JOB=<job_id> SPEED=1.5

Configuration

Etcha does not use a config file yet. Configuration is currently driven by environment variables and Make overrides.

Environment Variables

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

Make Variables

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"

Architecture

┌────────────────────────────────────────────────────────────────────┐
│                             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           │
└────────────────────────────────────────────────────────────────────┘

Troubleshooting

EtchaAgent.app was not found

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.app

If your app bundle lives elsewhere:

ETCHA_AGENT_APP_PATH="/absolute/path/to/EtchaAgent.app" \
  .derived/Build/Products/Debug/etcha "draw a moon"

CODEX_TIMEOUT

Codex generation took longer than the current hard timeout.

make show JOB=<job_id>
open ~/Etcha/jobs/<job_id>/codex.stderr.log

Then retry with a simpler prompt or author the tape manually.

CANDIDATE_MISSING or CANDIDATE_EMPTY

codex exec did not produce a usable candidate file.

which codex
make show JOB=<job_id>
open ~/Etcha/jobs/<job_id>/codex.stderr.log

Make sure codex is installed and runnable in the environment where EtchaAgent.app launches.

TAPE_PARSE_ERROR

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>

TAPE_OUT_OF_BOUNDS

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 screen saver shows only the preview demo

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.

Limitations

  • 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 codex CLI
  • Tape language is intentionally constrained and not suitable for arbitrary vector drawing

FAQ

Does Etcha require Codex?

Only for prompt-to-tape generation. Replaying existing tapes and manually authored tapes does not require Codex.

Where does Etcha store jobs?

By default in ~/Etcha, with per-job folders under ~/Etcha/jobs/.

Can I write tapes by hand?

Yes. That is an intended workflow. Edit tape.etcha, then run make validate.

What is the difference between tape.candidate.etcha and tape.etcha?

tape.candidate.etcha is raw model output. tape.etcha is the canonical tape copied into place only after validation succeeds.

Does the screen saver play failed jobs?

No. The screen saver cycles only through completed jobs with valid canonical tapes.

Can I run the agent without the Makefile?

Yes. The built app bundle is at .derived/Build/Products/Debug/EtchaAgent.app, and the CLI binary is at .derived/Build/Products/Debug/etcha.

Is there a license?

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

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 gh and 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.

Testing

make test

This runs the current EtchaTests bundle, which covers queued job defaults, successful candidate promotion, validation failure handling, and out-of-bounds rejection.

License

No license file is currently present in this repository.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors