6 notes
Architecture & composition
Decide what the shell owns, what a component may touch, and which work must stay outside the app.
- Treat the server as an optional decorator
A peer-to-peer app may use a relay for durability, but it should not need one to start.
Clap counter · src/clap_counter_lustre.gleam · Step 01
- One shared core, two runtimes
Keep the shared core portable by connecting to the same document from the BEAM and the browser.
Dice CLI · src/dice_cli.gleam · Step 01
- Keep latency-critical loops out of the update path
Let a real-time loop read a plain snapshot. Do not make it wait on the application.
Drum machine · src/drum_machine_lustre/audio_ffi.mjs · Step 05
- Hand a rendering surface to FFI, and bootstrap on Connected
Let an FFI module own the canvas pixels, and create shared channels only after the connection opens.
Pixel canvas · src/pixel_canvas_lustre.gleam · Step 01
- Stamp the schema; refuse bad reads
Fill and stamp a typed map before attaching it, so an incompatible reader gets an error.
Scoreboard CLI · src/scoreboard_cli.gleam · Step 02
- Panels take a TypedMap, never a root
Give a reusable component a typed map, whether that map is a root or a child. Keep document-wide effects in the shell.
Nested app showcase · src/showcase_lustre.gleam · Step 01
6 notes
Conflicts & consistency
Two clients change the same state at once. Keep the UI honest while the shared structure settles the result.
- Propose on release, render the pending signoff
Send one consensus proposal per gesture, then show whose approval is still missing.
Drum machine · src/drum_machine_lustre.gleam · Step 04
- Fallible edits render; never assert on a mutation
Handle every index-based edit as fallible. A peer may change the list between render and click.
Collaborative playlist · src/playlist_lustre/component.gleam · Step 02
- When a move is not atomic, crown one channel authoritative
A move across channels is not atomic. Choose one source of truth and reconcile the rest while rendering.
Retro board (full) · src/retro_board_lustre/board.gleam · Step 02
- Seed idempotently with Claims
Let every client seed the same initial values through first-writer-wins claims.
Collaborative Sudoku · src/sudoku_lustre/component.gleam · Step 01
- Anchors, not offsets
Store an anchor instead of a text offset, then resolve its current position after each edit.
Shared text editor · src/text_lustre/component.gleam · Step 02
- Show writes that have not settled
When writes are not optimistic, show them as pending until the confirming event arrives.
Tournament bracket · src/tournament_bracket_lustre.gleam · Step 04
2 notes
Presence & coordination
Some facts belong to the session, not the document: who's here, who drives, and what they need to tell each other.
- The minimal presence idiom
Declare one presence effect, use one typed payload, and remove the local session before the roster enters your model.
Retro board (tutorial) · src/retro_tutorial_lustre.gleam · Step 05
- Ride an application protocol on ripples
Send short-lived coordination over ripples, not through a document channel.
Grocery triptych · src/grocery_triptych_lustre/scenario_protocol.gleam · Step 05
3 notes
Testing & diagnostics
Reproduce the failures that matter, and read the runtime's own diagnostics before you guess at a sync bug.
- Sample diagnostics on every event
Put the runtime's diagnostics on screen before you debug synchronization.
Collaborative dice · src/dice_lustre.gleam · Step 01
- Extract pure modules; test without a server
Move decisions into pure modules so most tests need no document, sluice, or server.
Grocery triptych · src/grocery_triptych_lustre/refresh_guard.gleam · Step 06
- Test client death deterministically
Test a client dying mid-job with an in-process disconnect that produces the same leave event as the server.
Work queue · test/queue_semantics_test.gleam · Step 06