All
Hardware
Maps
ML & AI
Music
Social
Tools
Writing
Apps & Games
Deciduous
Decision graph CLI for tracing how software decisions evolve
RustSQLiteHTML/JS
Deciduous Archaeology Demo
Demonstrations of decision archaeology on React and stacked git workflows
RustReactGit
Phish Explorer
Jam analytics dashboard for Phish 3.0 era
ElixirPhoenix LiveViewD3.js
Local LLM on MacBook
4-bit quantization, safetensors, and Bumblebee + EMLX for Apple Silicon
ElixirRustPython
A Bot that posts like me
Porting the posting bot to Elixir using local LLM work
ElixirBumblebeeEMLX
Receipt Printer Software Suite
A complete software suite for thermal receipt printers
ElixirPython
Role Call
TV writer overlap explorer
ElixirPhoenix LiveView
Fill Your Sky
Interactive map of 418+ Bluesky communities with 545K+ people
ElixirPhoenix LiveViewD3.js
Code Mirror
A live code mirror experiment
ElixirPhoenix LiveView
Pocket Pnin
A local LLM running on my iPhone, coming to the App Store for free
SwiftMLX
NYC Census Maps
Interactive census and PLUTO data visualization for New York City
ElixirPhoenix LiveViewLeaflet.js
MTA Bus Tracker
Real-time MTA bus and train tracking on an interactive map
ElixirPhoenix LiveViewLeaflet.js
Concert GIF Maker
Extract GIFs from concert videos with a retro Mac interface
ElixirPhoenix LiveViewFFmpeg
Send a VERY direct message, to my receipt printer
A social project where friends send photos that print on my receipt printer
ElixirPhoenix LiveView
Archive TV
A real over-the-air TV channel from magnetic media archives
ElixirFFmpeg
Losselot
Neural network loss function explorer
Python
Todoinksies
A personal todo app
ElixirPhoenix LiveView
Ormery
An ORM written in Temper
Temper
Collage Maker
Upload photos and arrange them into grid collages
ElixirPhoenix LiveView
GenStage Tutorial 2025
A modern GenStage tutorial for the Elixir ecosystem
ElixirGenStage
Temper Rust Bug
Found a bug in the Temper compiler and built a demo repo
TemperRust
300+ Years of Tree Law
A blog post that became its own LiveView application
ElixirPhoenix LiveView
HEEx in Other Languages
Experiments porting Phoenix HEEx templates to Rust, Lua, C#, Java, Python, and JS
TemperRustLua
Live Draft LSP
Live-stream blog drafts from Zed to Phoenix via a custom LSP
RustElixirZed
Bluesky Hoover Apps
Various apps that vacuum up and process the Bluesky firehose
ElixirPhoenix LiveView
Bobby Posts Bot
A bot that posts like me, in Python
Python
Photo Booth Receipt Printer
A portable photo booth that prints on receipt paper
PythonElixir
Nathan For Us
A Nathan For You social network with video search and GIF creation
ElixirPhoenix LiveViewFFmpeg
Browser History Roast MCP
An MCP server that roasts you based on your browser history
PythonMCP
GenStage Tutorial (Original)
The original GenStage tutorial
ElixirGenStage
Bluesky Firehose Toys
Real-time firehose visualizations: emoji streams, jetstream comparisons, and more
ElixirPhoenix LiveViewWebSocket
31 exhibits
Apps & Games
MY FAV LEICA SHOTS
Work Log — Recent Commits
+12-4temperlang/temper/mainMar 18 18:08
a295294fix: reset for-loop variable on each iteration in Rust async state machine (#379)
Fixes #378
When a `for (var i = 0; ...)` loop is inside a while loop and the for
body contains an `await`, the Rust backend's coroutine-to-state-machine
transformation extracts the variable declaration to a persistent struct
field but drops the initializer from the case body. This means `i = 0`
only runs once (at generator creation), so the inner loop silently never
executes after the first outer iteration.
The fix: don't extract the initializer for ValueLeaf (simple value)
declarations. Extract just the declaration to persistent storage, and
leave the `i = 0` assignment in the state machine case body so it
re-executes every time the case is entered.
Before:
persistent: `var i: Int = 0` (runs once)
case body: (nothing)
After:
persistent: `var i: Int` (declaration only)
case body: `i = 0` (runs on each case entry)
This is safe for non-loop declarations too — the assignment just runs
once when the case is entered, same as before.
Minimal reproduction (pure Temper, no deps):
```
while (outerCount < 3) {
outerCount = outerCount + 1;
for (var i = 0; i < items.length; ++i) {
totalLoopBodyRuns = totalLoopBodyRuns + 1;
await resolved();
}
}
```
JS (correct): total loop body runs: 9
Rust before: total loop body runs: 3
Rust after: total loop body runs: 9
All existing be-rust and be-js tests pass.
57315carelease: v0.13.13 - sort narratives by most recent activity
Narratives in the web viewer are now sorted by most recent activity
(latest node timestamp) instead of tree size, across all modes:
goals, significant, branches, and hubs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
9925de1release: v0.13.12 - web viewer defaults to showing all goals
Changed default narrative mode from 'significant' (10+ nodes only)
to 'goals' (all goals) so the viewer page shows everything by default.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
45905abrelease: v0.13.11 - always-on version check with semver-aware messaging
Version checking is now always-on (removed opt-in toggle). Notifications
vary by severity: patch updates get a quiet one-liner, minor/major updates
get a prominent banner encouraging upgrade.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ed3c05dfeat: opt-in auto-update version check hook (#179)
* feat: opt-in auto-update hook system
- Add version-check hook for Claude Code, OpenCode, and Windsurf
- Hook checks crates.io once per 24h (rate-limited, 3s timeout)
- Opt-in via config: `deciduous auto-update on/off`
- Non-blocking (exit 0) - AI assistant informs user conversationally
- Results cached in .deciduous/.latest_version
- Disabled by default (auto_check = false in config.toml)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* default auto_check to true, add --no-auto-update flag to init
Auto version checking is now on by default for new projects.
Users can opt out during init with --no-auto-update, or toggle
later with deciduous auto-update off.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: document auto-update feature across all docs and tutorials
- README.md: auto-update section, commands reference, hooks table
- CLAUDE.md: CLI commands table, quick reference, session checklist
- docs/QUICK_REFERENCE.md: session recovery, file locations table
- docs/tutorial/reference: command table, detailed command docs, config
- docs/tutorial/getting-started: init config description
- src/init/templates.rs: CLAUDE_MD_SECTION session checklist
- src/opencode.rs: CLI commands, session checklists (both locations)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* format on push to not-main
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
52c431afix: reset for-loop variable on each iteration in Rust async state machine
Fixes #378
When a `for (var i = 0; ...)` loop is inside a while loop and the for
body contains an `await`, the Rust backend's coroutine-to-state-machine
transformation extracts the variable declaration to a persistent struct
field but drops the initializer from the case body. This means `i = 0`
only runs once (at generator creation), so the inner loop silently
never executes after the first outer iteration.
The fix: don't extract the initializer for ValueLeaf (simple value)
declarations. Extract just the declaration to persistent storage, and
leave the `i = 0` assignment in the state machine case body so it
re-executes every time the case is entered.
Before:
persistent: var i: Int = 0 (runs once)
case body: (nothing)
After:
persistent: var i: Int (declaration only)
case body: i = 0 (runs on each case entry)
This is safe for non-loop declarations too — the assignment just runs
once when the case is entered, same as before.
Minimal reproduction (pure Temper, no deps):
while (outerCount < 3) {
outerCount = outerCount + 1;
for (var i = 0; i < items.length; ++i) {
totalLoopBodyRuns = totalLoopBodyRuns + 1;
await resolved();
}
}
JS (correct): total loop body runs: 9
Rust before: total loop body runs: 3
Rust after: total loop body runs: 9
All existing be-rust and be-js tests pass.
Signed-off-by: Robert Grayson <bobbbygrayson+github@gmail.com>
48c6bf8fix: reset for-loop variable on each iteration in Rust async state machine
Fixes #378
When a `for (var i = 0; ...)` loop is inside a while loop and the for
body contains an `await`, the Rust backend's coroutine-to-state-machine
transformation extracts the variable declaration to a persistent struct
field but drops the initializer from the case body. This means `i = 0`
only runs once (at generator creation), so the inner loop silently
never executes after the first outer iteration.
The fix: don't extract the initializer for ValueLeaf (simple value)
declarations. Extract just the declaration to persistent storage, and
leave the `i = 0` assignment in the state machine case body so it
re-executes every time the case is entered.
Before:
persistent: var i: Int = 0 (runs once)
case body: (nothing)
After:
persistent: var i: Int (declaration only)
case body: i = 0 (runs on each case entry)
This is safe for non-loop declarations too — the assignment just runs
once when the case is entered, same as before.
Minimal reproduction (pure Temper, no deps):
while (outerCount < 3) {
outerCount = outerCount + 1;
for (var i = 0; i < items.length; ++i) {
totalLoopBodyRuns = totalLoopBodyRuns + 1;
await resolved();
}
}
JS (correct): total loop body runs: 9
Rust before: total loop body runs: 3
Rust after: total loop body runs: 9
All existing be-rust and be-js tests pass.
Signed-off-by: Robert Grayson <bobbbygrayson+github@gmail.com>
3683385fix: reset for-loop variable on each iteration in Rust async state machine
Fixes #378
When a `for (var i = 0; ...)` loop is inside a while loop and the for
body contains an `await`, the Rust backend's coroutine-to-state-machine
transformation extracts the variable declaration to a persistent struct
field but drops the initializer from the case body. This means `i = 0`
only runs once (at generator creation), so the inner loop silently
never executes after the first outer iteration.
The fix: don't extract the initializer for ValueLeaf (simple value)
declarations. Extract just the declaration to persistent storage, and
leave the `i = 0` assignment in the state machine case body so it
re-executes every time the case is entered.
Before:
persistent: var i: Int = 0 (runs once)
case body: (nothing)
After:
persistent: var i: Int (declaration only)
case body: i = 0 (runs on each case entry)
This is safe for non-loop declarations too — the assignment just runs
once when the case is entered, same as before.
Minimal reproduction (pure Temper, no deps):
while (outerCount < 3) {
outerCount = outerCount + 1;
for (var i = 0; i < items.length; ++i) {
totalLoopBodyRuns = totalLoopBodyRuns + 1;
await resolved();
}
}
JS (correct): total loop body runs: 9
Rust before: total loop body runs: 3
Rust after: total loop body runs: 9
All existing be-rust and be-js tests pass.
Signed-off-by: Robert Grayson <bobbbygrayson+github@gmail.com>
a2270c2fix: reset for-loop variable on each iteration in Rust async state machine
Fixes #378
When a `for (var i = 0; ...)` loop is inside a while loop and the for
body contains an `await`, the Rust backend's coroutine-to-state-machine
transformation extracts the variable declaration to a persistent struct
field but drops the initializer from the case body. This means `i = 0`
only runs once (at generator creation), so the inner loop silently
never executes after the first outer iteration.
The fix: don't extract the initializer for ValueLeaf (simple value)
declarations. Extract just the declaration to persistent storage, and
leave the `i = 0` assignment in the state machine case body so it
re-executes every time the case is entered.
Before:
persistent: var i: Int = 0 (runs once)
case body: (nothing)
After:
persistent: var i: Int (declaration only)
case body: i = 0 (runs on each case entry)
This is safe for non-loop declarations too — the assignment just runs
once when the case is entered, same as before.
Minimal reproduction (pure Temper, no deps):
while (outerCount < 3) {
outerCount = outerCount + 1;
for (var i = 0; i < items.length; ++i) {
totalLoopBodyRuns = totalLoopBodyRuns + 1;
await resolved();
}
}
JS (correct): total loop body runs: 9
Rust before: total loop body runs: 3
Rust after: total loop body runs: 9
All existing be-rust and be-js tests pass.
4dc6928feat: bots randomly pick JS, Rust, or Python compiled client
Each AI bot spawns a random compiled Temper client (JS, Rust, or
Python) as a subprocess and controls it by piping directions to
stdin and reading rendered frames from stdout.
The bot wrapper:
1. Picks a random backend (or accepts one as CLI arg)
2. Spawns the compiled client as a child process
3. Reads ASCII frames from the child's stdout
4. Sends frames to Ollama with personality prompt
5. Writes the LLM's chosen direction to the child's stdin
This means each bot is genuinely running a different language's
compiled output — a Rust binary, a Node.js script, or a Python
program — all connecting to the same server via WebSocket.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
d6f31aafeat: add AI snake bots powered by local LLMs via Ollama
Each bot connects as a regular WebSocket client — the server can't
tell it apart from a human. The bot receives rendered ASCII frames,
strips ANSI escapes, sends the board to a local Ollama model with a
personality-specific system prompt, parses the response for a
direction (u/d/l/r), and sends it to the server.
6 personalities: greedy (chase food), cautious (survive), aggressive
(hunt other snakes), chaotic (unpredictable), hunter (target the
leader), wall_hugger (patrol edges).
Usage:
node bot/snake-bot.js greedy mistral-small
bash bot/swarm.sh llama3.2:3b # launches 4 bots at once
All bots share one model in Ollama (~2GB for 3B params). Each bot
decides every ~1-2s. Between decisions, the snake keeps moving in
its current direction — gives a natural "thinking" feel.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
7de9814fix: nullable string comparison in spectator handshake for Rust backend
The Rust codegen can't compare String? directly to a string literal.
Use `is String` type check first, then compare the narrowed value.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
be3cf48feat: add spectator mode and join/spectate handshake protocol
Clients now send a handshake message on connect:
- "join" → player (spawns a snake)
- "spectate" → spectator (receives frames, no snake)
Server waits for the first message to decide. Spectators get added
to the broadcast list but no PlayerSnake is created. They see the
full board with all players and bots but don't affect the game.
New file: bot/spectate.js — minimal spectator client.
Updated: client sends "join", bot sends "join", spectate sends
"spectate".
Usage:
node bot/spectate.js # watch the game
node bot/spectate.js ws://host:8080 # watch a remote game
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
0329d61feat: randomize snake spawn positions with edge buffer
Spawn positions are now PRNG-driven instead of cycling through 4
fixed quarter positions. Each snake gets a random position at least
5 cells from any edge (so it has room to react before hitting a
wall) and a random direction. The seed is mixed with the player
index for deterministic but spread-out placement.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
aaf8625feat: add multiplayer snake over WebSockets
Add networked multiplayer to the snake game. One player starts a server,
others connect as clients, and everybody shares the same board. The
entire multiplayer stack — game logic, protocol, server, client — is
written in Temper and compiles to every backend.
This commit covers the game-side changes. The compiler-side changes
(adding std/ws and terminal size detection to std/io) live on the
`do-more-crimes-to-play-snake-multiplayer` branch in the Temper repo.
## What changed
### Multi-snake game logic (src/snake.temper.md, +437 lines)
New types:
- `PlayerStatus` (sealed): `Alive` / `Dead`
- `PlayerSnake`: id, segments, direction, score, status
- `MultiSnakeGame`: width, height, list of snakes, food, rng, tick count
New functions:
- `newMultiGame(width, height, numPlayers, seed)` — spawns snakes at
spread-out positions around the board (quarters, facing inward)
- `multiTick(game, directions)` — the core multiplayer tick. Handles:
- Per-snake direction changes (rejects opposite)
- Wall collision (per snake)
- Self collision (per snake)
- Head-to-body collision (your head hits another snake's body)
- Head-to-head collision (two snakes move to the same cell — both die)
- Food eating (first alive snake to reach food gets the point)
- Food respawning (avoids all snake segments)
- `multiRender(game)` — renders the board with per-player symbols:
P0: @/o, P1: #/+, P2: $/~, P3: %/=, P4+: &/.
Shows score and alive/dead status per player below the board.
- `changePlayerDirection(game, playerId, dir)` — updates one player
- `isMultiGameOver(game)` — true when <=1 snake alive (2+ players)
- `addPlayer(game, seed)` — adds a snake to a running game
- `removePlayer(game, playerId)` — removes a disconnected player
- `directionToString` / `stringToDirection` — serialization helpers
All single-player code is untouched. The 18 original tests still pass.
### Server (server/server.temper.md)
A WebSocket server using the new `std/ws` module. Architecture:
- Accept loop (async block): listens on port 8080, accepts connections,
calls `addPlayer` to spawn a new snake, then spawns a per-connection
recv loop as another async block
- Per-connection recv loop (async block per player): reads single-char
direction messages (u/d/l/r), calls `changePlayerDirection`
- Game loop (async block): ticks every 200ms, builds the directions
list from current snake states, calls `multiTick`, broadcasts the
rendered frame to all connections via `wsSend`
The board size is calculated from the server's terminal dimensions
minus a 10-character margin on each axis, using the new
`terminalColumns()` and `terminalRows()` from `std/io`.
Players can join at any time. The server handles disconnections
gracefully (the snake just stops getting direction updates and
eventually hits a wall).
### Client (client/client.temper.md)
A WebSocket client that connects to the server. Two async blocks:
- Input loop: reads w/a/s/d via `readLine()`, maps to single-char
direction codes (u/d/l/r), sends via `wsSend`
- Recv loop: receives messages via `wsRecv`, prints them directly
(the server sends fully-rendered frames as plain text)
The client is intentionally dumb. It has no game logic. It sends
keypresses and displays whatever the server sends back.
### Protocol
Deliberately minimal, no parsing required:
- Client → Server: single characters "u", "d", "l", "r"
- Server → Client: complete rendered ASCII frames (the output of
`multiRender`), sent as-is over the WebSocket
No JSON. No message framing. No serialization library. The server
renders the board, sends the string, the client prints it.
### Tests (test/snake_test.temper.md, +13 tests)
New tests covering: multi-game creation, snake count, alive status,
different spawn positions, segment count, both snakes moving on tick,
wall collision killing a snake, game-over detection, direction changes,
opposite direction rejection, addPlayer, removePlayer, render output,
and direction serialization round-trips.
Total: 31 tests (18 single-player + 13 multiplayer). All pass.
## How to play
# Terminal 1: start server
cd temper.out/js && node snake-server/index.js
# Terminal 2: player 1
cd temper.out/js && node snake-client/index.js
# Terminal 3: player 2
cd temper.out/js && node snake-client/index.js
Connect as many players as you want.
## Compiler changes required (separate branch)
The Temper compiler gained a new `std/ws` module with 6 @connected
functions and 2 opaque types, wired for JS (`ws` npm package) and
Rust (`tungstenite` crate). Also `terminalColumns()`/`terminalRows()`
in `std/io`. 13 files changed, 554 insertions. Committed on branch
`do-more-crimes-to-play-snake-multiplayer` in the Temper repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
4860666fix: avoid await inside broadcast loop (Rust codegen state machine bug)
The Rust backend's state machine doesn't reset for-loop variables when
the outer loop iterates after an await inside the inner loop. Work
around this by using for-of (forEach) without await for the broadcast,
since wsSend is synchronous in the Rust backend (channel push).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
8f1bc83docs: detailed report on Rust backend broadcast loop await bug
Documents a codegen bug where for-loop variables inside outer loops
don't reset when the inner loop awaits a synchronously-resolved
Promise. Includes root cause analysis (re-entrant generator.next()
from inline on_ready callbacks), the exact state machine case flow,
evidence from debug logging, the workaround, and scope analysis.
Verified the bug does not reproduce with async-resolved Promises
(sleep(0)) — only with @connected functions that call pb.complete()
synchronously (like wsSend). The JS backend is unaffected.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
c52a6fcdocs: rewrite bug report referencing only Temper main
Clean report with no references to snake, WebSocket, or multiplayer
code. Minimal reproduction uses only core Temper primitives
(PromiseBuilder, async/await, for loop). Tested on main at cb8c3d5.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ca39530docs: add JS, Rust, and Python multiplayer setup instructions
Complete setup and run instructions for all three backends with
server and client commands. Documents mix-and-match interop.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
cdcf29ddocs: update bug report with minimal repro on Temper main
Confirmed the for-loop variable reset bug on Temper main (cb8c3d5)
with a pure-Temper reproduction — no std/io, no WebSocket, no
external dependencies. Just PromiseBuilder + await inside a for
loop inside a while loop.
JS: "total loop body runs: 9" — PASS
Rust: "total loop body runs: 3" — FAIL
Root cause: `for (var i = 0; ...)` compiles the init as a struct
field default (runs once at generator creation), not as a
per-iteration assignment in the state machine.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
11a0d08fix: update compiler branch refs to do-more-crimes-to-play-snake-multiplayer
CI and build instructions now point to the new Temper compiler branch
that includes std/ws (WebSocket) and std/io terminal size detection.
Updates the git clone in the GitHub Actions workflow, the generated
child repo READMEs, and the manual build instructions in README.md.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2365282docs: update bug report with minimal repro on Temper main
Confirmed the for-loop variable reset bug on Temper main (cb8c3d5)
with a pure-Temper reproduction — no std/io, no WebSocket, no
external dependencies. Just PromiseBuilder + await inside a for
loop inside a while loop.
JS: "total loop body runs: 9" — PASS
Rust: "total loop body runs: 3" — FAIL
Root cause: `for (var i = 0; ...)` compiles the init as a struct
field default (runs once at generator creation), not as a
per-iteration assignment in the state machine.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
bbe65f6docs: add JS, Rust, and Python multiplayer setup instructions
Complete setup and run instructions for all three backends with
server and client commands. Documents mix-and-match interop.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1e0cf3cfix: avoid await inside broadcast loop (Rust codegen state machine bug)
The Rust backend's state machine doesn't reset for-loop variables when
the outer loop iterates after an await inside the inner loop. Work
around this by using for-of (forEach) without await for the broadcast,
since wsSend is synchronous in the Rust backend (channel push).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
d46f2f7docs: detailed report on Rust backend broadcast loop await bug
Documents a codegen bug where for-loop variables inside outer loops
don't reset when the inner loop awaits a synchronously-resolved
Promise. Includes root cause analysis (re-entrant generator.next()
from inline on_ready callbacks), the exact state machine case flow,
evidence from debug logging, the workaround, and scope analysis.
Verified the bug does not reproduce with async-resolved Promises
(sleep(0)) — only with @connected functions that call pb.complete()
synchronously (like wsSend). The JS backend is unaffected.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
07dec9cdocs: rewrite bug report referencing only Temper main
Clean report with no references to snake, WebSocket, or multiplayer
code. Minimal reproduction uses only core Temper primitives
(PromiseBuilder, async/await, for loop). Tested on main at cb8c3d5.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1fe9a5aAdd Neovim plugin source, build output, and CI workflow
- nvim/: plugin source files (ftdetect, plugin, highlighting logic)
- temper.out/lua/: committed build output for CI testing
- .github/workflows/: CI that runs Lua tests and publishes to
nvim_temper_syntax_highlighter on success
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
bc2e9fdfeat: add backend-specific compiler change details to child repo READMEs
Each of the 6 published repos now explains exactly what had to change
in the Temper compiler to make that backend work:
- JS: auto-connected pattern, setTimeout + process.stdin raw mode
- Python: ThreadPoolExecutor futures, tty/termios raw mode
- Lua: cooperative coroutine scheduler, non-blocking IO polling
- Rust: cross-crate FunctionCall paths, dependency detection fix
- C#: native async/await, net8.0 framework, namespace fixes
- Java: CompletableFuture + ForkJoinPool, timeout fix, stty raw mode
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
d27a2fdfix: update CI workflow with correct run commands for all backends
- Python: add std to PYTHONPATH, update prereqs to 3.11+
- Java: add run.sh script (installs local Maven deps then runs)
- Java run_cmd in README now points to run.sh instead of incomplete mvn command
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
4e085e3fix: make all 6 backends run the snake game correctly
Lua:
- Replace temper.TODO stub with cooperative coroutine scheduler
- async {} now compiles to temper.async_launch() (was "TODO")
- LuaTranslator emits temper.run_scheduler() after top-level code
- Non-blocking IO: sleep uses deadline-based promises, readLine uses
stty min 0 time 0 for polling
- Round-robin scheduler drives multiple async blocks cooperatively
Rust:
- Fix missing temper-std dependency in generated Cargo.toml
- Connected functions (stdSleep, stdReadLine) reference temper_std::
paths but bypassed the import-based dependency scan
- RustTranslator now tracks usedSupportFunctionPaths
- RustBackend scans these after translation to inject temper-std dep
with correct features
- Also fixes missing temper_std::init() in generated lib.rs
- Add raw terminal mode for single-keypress input
Java:
- Fix waitUntilTasksComplete() 10-second hard timeout
- Now loops until ForkJoinPool is truly quiescent
- Add raw terminal mode via stty for single-keypress input
C#:
- Update target framework from net6.0 to net8.0 (current LTS)
- Namespace-qualify OrderedDictionary and AsReadOnly in RegexSupport.cs
to avoid conflicts with System.Collections.Generic.OrderedDictionary
introduced in .NET 9+
- Add single-keypress input via Console.ReadKey
Python:
- Add raw terminal mode for single-keypress input via tty/termios
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ca3191ctest: add functional test for `std/io` `sleep()` across backends
Adds `control-flow/io-sleep/io-sleep.temper.md` to the functional test
suite, verifying that `sleep()` from `std/io` works correctly:
- Sleep returns and execution continues after `await`
- Multiple sequential sleeps work
- Zero-ms sleep resolves immediately
- Sleep interleaved with computation produces correct results
Uses short delays (5-10ms) to avoid slowing the test suite.
Passes on: JS, Python, Lua, Java 17, C#.
Skipped on Rust (`@Ignore`) because the Rust functional test
infrastructure only links `temper-core`, not `temper-std`, so
`import("std/io")` produces an unresolved crate at cargo build time.
The Rust `sleep` implementation itself works (verified manually via
the snake game with `cargo run`).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
8356001docs: add detailed compiler modification context for Rust backend
Explains what was changed in the Temper compiler (commit 0f31c89) to make
this game possible — RustSupportNetwork.kt, RustBackend.kt, and
std/io/support.rs with thread::sleep + PromiseBuilder pattern.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
644acf8docs: add detailed compiler modification context for C# backend
Explains what was changed in the Temper compiler (commit 0f31c89) to make
this game possible — StandardNames.kt, CSharpSupportNetwork.kt,
CSharpBackend.kt, and std/Io/IoSupport.cs with native async Task.Delay.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
e647da7docs: add detailed compiler modification context for Python backend
Explains what was changed in the Temper compiler (commit 0f31c89) to make
this game possible — PySupportNetwork.kt and temper_core/__init__.py with
ThreadPoolExecutor-based sleep and Future-based readLine.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
212eed0docs: add detailed compiler modification context for Lua backend
Explains what was changed in the Temper compiler (commit 0f31c89) to make
this game possible — temper-core/init.lua with synchronous blocking sleep,
temper.TODO() async stub, and make_resolved() pattern for await translation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
75a4c9ddocs: add detailed compiler modification context for JavaScript backend
Explains what was changed in the Temper compiler (commit 0f31c89) to make
this game possible — JsSupportNetwork.kt, JsBackend.kt, and temper-core/io.js
with setTimeout-based sleep and raw mode stdin readLine.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
389ab33docs: add detailed compiler modification context for Java backend
Explains what was changed in the Temper compiler (commit 0f31c89) to make
this game possible — StandardNames.kt, JavaSupportNetwork.kt, and Core.java
with ForkJoinPool Thread.sleep + CompletableFuture.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
c8c27abdocs: add KNOWN_ISSUES.md for Rust backend dep resolution bug
Documents the temper-std missing dependency issue when multi-module
projects import from std/io in a secondary module.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
9218db1feat: real-time keyboard input via two async blocks
Split into 3 pure Temper modules:
- src/ (snake library): pure game logic, types, render, PRNG
- game/ (snake-game runner): two async blocks for stdin input + game loop
- test/ (snake-test): 18 tests, separated to avoid readLine blocking
Game uses w/a/s/d + Enter for direction control. Input loop and game
loop cooperate via shared mutable direction variable.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
38e266aFix native test detection: install pytest, use pipefail
Two bugs prevented Python test failures from being caught:
1. pytest was never installed — pip only installed the ORM packages
2. pipe to tee swallowed exit codes — tee always succeeds, masking
the actual test runner exit code
Fixes: add `pip install pytest` before running tests, add
`set -o pipefail` to all native test steps so failures propagate.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
106f24aRun native runtime tests on generated code before publishing
Test matrix now runs both `temper test -b <lang>` AND native tests
(pytest, npm test, cargo test, etc.) against temper.out/<lang>/.
Publish is gated on native test results. Issues auto-opened only
when temper tests pass but native tests fail (codegen bug signal).
Reverts notify-app-template to simple vendor push since all testing
now happens in the top-level alloy repo.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
"Intensely melodic, gorgeous, charming version that gets repetitious and nearly t..."
TAP TO SEE AND PLAY JAMS
Simple
.413
TAP TO FLIP BACK
38 jams
★
2010-08-06
15:45
William Randolph Hearst Greek Theatre, University of California, Berkeley · Berkeley, CA
Intensely melodic, gorgeous, charming version that gets repetitious and nearly transcendent.
★
2011-01-01
11:45
Madison Square Garden · New York, NY
Glorious version that, while not departing particularly far from the typical "Simple" structure, does have a unique closing coda-like structure that almost sounds like a new song. Rave reviews from fans.
★
2011-07-01
14:16
Watkins Glen International · Watkins Glen, NY
A straightforward but delicately played "Simple" fades away, leading to 6 minutes of '99-esque space.
★
2012-06-23
9:21
First Niagara Pavilion · Burgettstown, PA
The typical jam breaks into a short dreamy, lightly pulsing abstraction, before settling into extended transition space and > to "Light."
★
2013-07-13
8:43
Merriweather Post Pavilion · Columbia, MD
Much more of a rocking outro jam than the standard with solid Trey who develops a great riff.
★
2013-12-30
10:24
Madison Square Garden · New York, NY
Essentially straightforward, but the outro jam is a little different than the norm, is nicely played, and includes teases of "The Line," "Fuego," and "Magilla" from Page.
★
2014-08-29
21:53
Dick's Sporting Goods Park · Commerce City, CO
The Rhythm Department gets the credit for propelling this jam beyond the standard and into a rocking and increasingly euphoric groove that recalls several standout '96 versions. Then Fish, Page and Mike drive the jam into a funky and rhythmic realm.
★
2015-07-22
14:09
Les Schwab Amphitheater · Bend, OR
The typically joyful "Simple" jam makes a turn to the Dark Side, as Trey leads the band into a stark exploration with a somewhat Pink Floyd-like tone. Listen for Mike's approval bell.
★
2015-08-21
10:15
Watkins Glen International · Watkins Glen, NY
The Magnaball-opening "Simple" wastes no time in getting the festivities underway. After some standard jamming in the typical chord progression, the band opts not to resolve to F and instead stays in B-flat, churning out a fuzzy, pulsating groove led by the rhythm section.
★
2016-09-03
15:47
Dick's Sporting Goods Park · Commerce City, CO
For the second time in three years, the Dick's "Simple" is a real treat. Some great melodic delay work and alien noises from Trey provide a backdrop to interwoven melodies from Page and Mike (who also drops some bombs of approval), and Fish finds a fun marching beat to lead the way.
★
2016-10-22
16:02
Verizon Wireless Amphitheatre at Encore Park · Alpharetta, GA
The first "Simple" of Fall Tour picks up right where Dick's left off. The jam moves to dark but mellow space, and after some probing, Trey finds a magical riff that he just won't let go. By the 11 minute mark, his bandmates have all hopped the gravy train and the gearshift is set for Interstellar Overdrive. Fishman aptly suggests "Saw It Again" in the post-peak decompression, but Trey has other ideas (> "46 Days").
★
2017-07-15
27:10
Huntington Bank Pavilion at Northerly Island · Chicago, IL
A wide-ranging improvisational monster that moves from low-key grooves to powerful heavy rocking to a "Timber (Jerry)"-esque tribal zone to a majestic major-key finale with some lovely Trey trills. Must-hear stuff, no ifs, ands, or buts.
★
2017-08-06
25:26
Madison Square Garden · New York, NY
A multifaceted capstone, assuredly anchoring the final show of the historic Baker's Dozen run. Luxuriating in each movement, the playing morphs from dark, electro-funk, to brighter, swinging, groovy fun, and then to appropriately celebratory hose. The final winding down is a fitting glaze for this big doughnut.
★
2017-09-02
13:46
Dick's Sporting Goods Park · Commerce City, CO
After running through familiar ground, the jam gets rocking, then gradually settles down into some lower intensity grooving, including some nice Page/Trey interplay. Lacking a peak or other point of punctuation, the jam seemingly winds down over several minutes.
★
2018-07-20
14:44
Gorge Amphitheatre · George, WA
Quickly leaves blissful "Simple" jamming behind and steadily gains intensity with a dark undercurrent en route to a rocking peak and then returns to the song's riff to close.
★
2018-07-27
10:36
The Forum · Inglewood, CA
Gets beyond standard "Simple" in a hurry, first taking on a more rocking vibe and then traversing a groove producing themes reminiscent of the Island Tour Roses before spacing out and > "Ghost".
★
2018-10-21
21:05
Hampton Coliseum · Hampton, VA
A creeping late-night stroll accented by a descending progression and mysterious Trey soloing then smoothly transitions to bliss and ignites at minute 17 for a joyous, trill-filled peak before slamming back into "Simple" to end.
★
2019-06-25
19:34
Darling's Waterfront Pavilion · Bangor, ME
The typical outro dissipates into spacious twilight and then very gradually heats up while retaining its late-night feel as Trey's riffs get more rocking en route to a peak. A funky staccato section caps the jam before > "Piper".
★
2020-02-23
15:30
Moon Palace · Quintana Roo, Cancun, Mexico
Departs "Simple" with an "Under Pressure" tease and smoothly coasts through multiple short-lived grooves before another tease ("In-A-Gadda-Da-Vida") sets the table for the final climb.
★
2021-07-28
14:13
Walmart Arkansas Music Pavilion · Rogers, AR
-> out of "DWD". The playing grows delicate at first, then Trey switches to a heavier tone and effects start flying. The jam grows nastier and more atonal, with Fish really going to work, as things get sludgy and strange in a way reminiscent of certain Summer 2003 jams. Resolves into a surprising but superb -> into "Fuego". A real harbinger for "Simple"'s glorious Summer 2021.
★
2021-08-06
24:39
Ruoff Music Center · Noblesville, IN
Why we travel from points far and wide to Noblesville, Indiana. Pick a timestamp. 6.37 finds Trey alighting upon a lovely, carefree vibe. Jump to 10.35, and the band is moving through a passage of gnarly, tripped-up disco funk. 12.45 finds Trey (and listen to Page!) working his pedals like it's 1995. Or something. 15.15 features Trey shearing sound, the band deep in psychedelic play that makes recent Melts blush (the focus / the cohesion). Pick a point and it just might be your favorite .... Before the band, taking a victory lap, returns to simply sing us back home.
★
2021-09-05
8:34
Dick's Sporting Goods Park · Commerce City, CO
Launches a tremendously fun segment and lays down the musical bones that carry through to -> "Catapult", back to "Simple", and later "Meatstick".
★
2021-10-16
12:01
Chase Center · San Francisco, CA
A kaleidoscopic outro of interweaving delay riffs then shifts at 7:00 to a pumped-up syncopated groove, drawing similarities to "Simple's" 1996 heyday.
★
2022-02-26
12:53
Moon Palace · Quintana Roo, Cancun, Mexico
At 7:30 a sweet, easy-going bluesy groove takes hold (think "Spooky") and brings the jam to a close -> "SaSS". Not a style often seen, but wonderfully employed here.
★
2022-04-23
14:48
Madison Square Garden · New York, NY
Initially an upbeat jam that pleasantly bobs along until a switch is flipped around 13:30, launching into a segment of terrifying power and space-age effects -> "Egg in a Hole Jam"
★
2022-04-23
1:25
Madison Square Garden · New York, NY
-> Trey pulls out of the "Egg in a Hole Jam" with a deft return to the "Simple" riff for a triumphant conclusion to a mind-blowing segment.
★
2022-06-01
23:45
Credit One Stadium · Charleston, SC
Multi-faceted shapeshifter cruises through an array of styles ranging from heavenly bliss to industrial funk, eventually ascending to a sizable peak and then briefly returns to "Simple" to close.
★
2022-07-20
14:57
TD Pavilion at the Mann · Philadelphia, PA
> from "Soul Planet." Jam begins in familiar melodic territory...but then the band rips a portal in space and
time, finding themselves back in the psychedelic murk that occupied the previous set's "Split Open and Melt"
jam. Mike and Fishman consistently challenge the rhythm as Page's textural work and Trey's low octave
synth effect punctuate the whole affair. The Phish machine churns into a pulsing, monstrous
polyrhythmic jam that grooves like a mutha and pulls off a nice -> into "Light."
★
2022-08-06
12:42
Atlantic City Beach · Atlantic City, NJ
An "Under Pressure" tease shakes things loose, allowing for a wispy, cyclical jam to take hold, which ratchets up moderately before an eerie alien outro possibly setting up a > "2001" but instead > "BDTNL". Quite a nugget of improvisation.
★
2023-04-17
19:08
William Randolph Hearst Greek Theatre, University of California, Berkeley · Berkeley, CA
-> from another MASSIVE, must-hear Bay Area "Tweezer". The tempo is slow at first, then picks up as effects from multiple band members are toyed with. A change at 11:15 sends the jam to a gloriously rollicking place. The play from Trey is anthemic as the band takes a proverbial victory lap after dropping an hour of seriously impressive improv. The joy from the stage is audible. Things eventually wind down before > "Rock and Roll".
★
2023-08-25
21:51
Broadview Stage at SPAC · Saratoga Springs, NY
-> from an expansive "AWoH". This multi-sectioned journey initially takes off in a minor key and plants its flag there as Trey's looping and Mike's cascading high octave wash apply density and complexity. The grooves grow polyrhythmic before coalescing into a dark, intense, effects-laden attack. A seemingly composed rhythm shift then leads the band towards anthemic territory with fluid Trey playing, before yielding to an extended passage of midnight-dark ambience to close. Fantastic.
★
2023-12-28
14:01
Madison Square Garden · New York, NY
A tale of two "Simples". Providing a great contrast from a notable "Mike's", the band drops quickly into less by way of bebop to traverse more bucolic terrain. A welcome approach, the version rewards repeated listens-given that the extended improvisation is ineffable-before Trey subtly signals a change, leading the band with unusual, restrained soloing that builds but doesn't break.
★
2024-02-24
?
Moon Palace · Quintana Roo, Cancun, Mexico
This show-opener allows the band to stretch their jamming legs with a version that fluidly transitions beyond typical "Simple" and finds a spunky, synth-backed groove over which Trey riffs thematically before a final turn towards bliss and then -> "Sanity".
★
2024-07-27
40:51
Alpine Valley Music Theatre · East Troy, WI
This mammoth jam never drags or seemingly missteps while flowing so well that it may come across as deceivingly simple and stands as one of 2024's most illustrative testaments to the supremely complementary modern-day play style. More notable for the overall journey than individual moments as it reaches out in myriad directions and successfully latches onto practically each one. Longest "Simple" ever.
★
2024-12-28
13:03
Madison Square Garden · New York, NY
Opens the NYE run with a bang right out of the gates with a jam that first plays on the usual "Simple" theme with gusto and then flows directly into a more rhythmic transition segment. From there, the intensity quickly rises to a frenzy and concludes with a celebratory peak before returning home.
★
2025-07-16
13:10
TD Pavilion at the Mann · Philadelphia, PA
From the jump, bouncy summer synth-funk smacks against the walls of the Mann's pavilion as Page's synths sizzle in the hot summer air, before the groove coalesces around the "Macarena", of all things. Suddenly the crowd is transported back to the mid/late 90s as Trey uses the "Macarena" melody as the linchpin of an upbeat groove-fest, before the time portal then finally dissipates and > "Ether Edge".
★
2025-09-20
18:17
Hampton Coliseum · Hampton, VA
At a time when "Simple" seems more comfortable as a supportive piece of a set (7/27/24 notwithstanding) here it serves as the centerpiece of a Saturday Night Special in Hampton. A bubbly and bright exit from the song proper defined by Trey's use of chords and Page's cloud-like Rhodes playing, the jam rolls along in a dream-like space moving from hopeful melodies to creepy Phrygian tones. Coalescing around a snarling, effect-driven riff from Trey around 12min, Page's contrasting baby grand soon elevates the jam towards a more rock-driven peak that latches onto a sustained note from Trey before a choice fade > into a high-energy "A Wave of Hope."
★
2025-09-20
1:50
Hampton Coliseum · Hampton, VA
-> from AWOH. More than a simple return, this coda caps off a thick 40 min of improv that gives way to a notable "Beneath a Sea of Stars part 1".
Disappointed that War does not lead to actual combined-arms conflict.
jeff
04:01 AM
that would be hard to conjure
jeff
04:02 AM
I am so excited that this works and is a successful combination of windows and old apple lol
Uechi Nerd
04:02 AM
Probably for the best, actually. That shit is very very messy.
Uechi Nerd
04:02 AM
I am intrigued and happy it works!
Uechi Nerd
04:03 AM
I respect the wizardry.
Visitor7804
04:05 AM
this is delightful.
jeff
04:06 AM
hell yeah visitor 7804, this is livin' brother
guy4get
04:07 AM
i've never felt so alive
EarlofVincent
04:09 AM
Commencing experiment in 3....2....
jeff
04:14 AM
1
leah
04:16 AM
hi!
leah
04:16 AM
this is lovely
jeff
04:21 AM
hi! lol I was just like what if I combined Mac and windows and added a flower tree of life and called it my homepage and then smoked some weed and made it happen in an empty mall in Connecticut
B. Droptables
10:51 AM
Always cool to play with your toys.
Visitor1128
08:47 AM
yo!
Visitor1128
08:48 AM
i can barely work my phone. what am i doing here?
jeff
09:04 AM
the phone is not optimized yet but it "kind of works" I am sorry lol
jeff
09:04 AM
you have to pick a username, then it goes to the chat, then if you hit the bottom tabs it'll let you go to the app sections.
Bobdawg
04:43 AM
Hi everybody this is my blog I hope you enjoy it I did some more changes and anyone can write a post here now for me.
dinkleberg
01:45 AM
ALL HAIL TREE OF LIFE
jeff
08:55 PM
hi Hacker News
jeff
04:28 PM
hey there I am not really Jeff
Mal Function
05:34 PM
Hey! Please reveal... how exactly do I actually use losselot on my Mac? I've run the git clone commend in Terminal.app and seem successfully to have installed into a new <losselot> sub-folder in my home folder but now???