Ask an LLM a question, watch the answer flap onto a split-flap display.
clapper-ai is three boxes. The middle one never changes; the ends are plug-in
adapters you swap in config.yaml:
INPUT ANSWER DISPLAY
┌───────────┐ ┌───────────────────┐ ┌──────────────────┐
│ keyboard │ text │ ask LLM │ grid │ virtual board │
│ mic* │ ─────► │ question → grid │ ─────► │ Vestaboard* │
│ phone* │ │ validate │ │ DIY hardware* │
└───────────┘ └───────────────────┘ └──────────────────┘
* = later phase
Everything speaks one contract: a grid of tile codes — rows × cols ints
where 0 is blank, 1–26 are A–Z, 27–36 are digits, 63–69 are color
tiles (see docs/character-codes.md). Any input that
produces text and any display that renders a grid can join, which is why the
folders are laid out the way they are:
src/clapper_ai/adapters/inputs/— one file per input devicesrc/clapper_ai/adapters/displays/— one file (or folder) per display devicesrc/clapper_ai/domain/+src/clapper_ai/application/— the core; never imports an adapter
Adding a device later = one new file, one registry line, one config line. No core changes. See docs/add-your-own-board.md and docs/add-your-own-input.md.
Needs uv — it fetches Python 3.14 for you if you don't have it.
uv sync
uv run python -m clapper_aiOpen http://127.0.0.1:8000, type a line in the terminal, watch it flap. Full walkthrough in QUICKSTART.md.
The default fake LLM echoes what you type. For real answers:
uv sync --extra llm
export ANTHROPIC_API_KEY=sk-ant-... # PowerShell: $env:ANTHROPIC_API_KEY = "sk-ant-..."then set llm.type: anthropic in config.yaml for plain-text answers, or
llm.type: anthropic-smart to let the AI also design the board — centered
layouts with color accents, web search for live facts, and full-tile art
("paint the great wave"). Either way, that's the only change.
config.yaml pick your input / display / llm here
src/clapper_ai/
domain/ tile codes, Grid, LayoutSpec, text layout, grid rules
application/
interactors/ AnswerQuestion — the use case: one turn, question to board
ports/ the three protocols the app speaks
adapters/
inputs/ InputSource adapters (text_input.py today)
displays/ DisplaySink adapters (virtual/ today)
llm/ LLMClient adapters (echo.py — the `fake` type, anthropic_client.py)
docs/ architecture + how to add your own device
design/ design notes and implementation plans for larger changes
tests/
More depth in docs/architecture.md.
The idea came from this TikTok by @karenxcheng. I wanted a board of my own, but one that only prints your text straight back at you is a typewriter with extra steps.
What I actually wanted was to give the AI a canvas it can express itself on, the way it wants to. So the model does not only decide what the answer says, it decides how the answer should look. It picks the layout, centers the line that matters, adds color accents, or paints the whole board as art.
That is why the contract between brain and board is a LayoutSpec rather than a
string, and why render_layout() stays pure and deterministic. The model gets
free rein over the design, and the renderer still decides what is actually legal
on a 6x22 grid of tiles.
Try it with llm.type: anthropic-smart.
PRs welcome — read CONTRIBUTING.md first. MIT licensed; see LICENSE.