Skip to content

robherley/lava.watch

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

18 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

lava

A lava lamp simulation served two ways: as ANSI half-blocks over SSH, or as RGBA pixels on a <canvas> in the browser. One Rust simulation, two output formats, one self-contained static binary.

Run it locally

script/server

Then from any other terminal:

ssh -p 2222 localhost      # SSH transport
open http://localhost:8080/   # web transport (WASM in your browser)

script/server does three things on each invocation:

  1. script/setup β€” generates a dev SSH host key in .dev/ (gitignored) if missing.
  2. script/build-wasm β€” wasm-packs the engine to wasm32-unknown-unknown so lava-web can embed it.
  3. cargo run --release -p lava β€” runs the all-in-one binary that hosts both transports.

Pick a palette

By SSH username or by URL path β€” same parser, same aliases.

ssh ultraviolet@localhost   # or `uv@`, `blacklight@`
ssh pink@localhost          # β†’ bubblegum
ssh ice@localhost           # β†’ ocean
http://localhost:8080/aurora
http://localhost:8080/toxic
http://localhost:8080/uv

Anything that doesn't parse falls back to classic. Once connected, ← / β†’ cycles palettes (the new name flashes briefly in the bottom-left badge), and left-click anywhere on the lamp to heat that spot β€” nearby blobs warm up and rise.

For the full palette list + aliases, connect as help:

ssh help@localhost

Prints a colored cheat-sheet and disconnects (no PTY required, no connection slot consumed).

Configuration

All env vars; both transports read what they need from the same environment.

Var Type Default Used by Description
LAVA_PORT u16 2222 ssh SSH listen port
LAVA_HOST_KEY string (required) ssh Contents of an OpenSSH-format private host key (not a path)
LAVA_HOST_KEY_PASSWORD string (none) ssh Passphrase for LAVA_HOST_KEY if it's encrypted
LAVA_MAX_CONN_TIME u64 300 ssh Hard session timeout, in seconds
LAVA_MAX_PER_IP usize 3 ssh Concurrent SSH connections per IP
LAVA_SPEED f32 0.8 ssh Simulation speed multiplier (1.0 = engine "natural" rate, lower = slower)
LAVA_WEB_PORT u16 8080 web HTTP listen port
RUST_LOG string lava=info,… both tracing-subscriber filter

Logs are pretty-printed when stdout is a TTY and JSON otherwise. SSH events include peer (IP:port), cols/rows, term (client $TERM), banner (client SSH version), palette, duration_secs, and a structured reason (client_exit, timeout, disconnect, write_failed).

Architecture

                β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                β”‚        lava-engine         β”‚  pure Rust, no I/O
                β”‚  sim Β· palette Β· session   β”‚
                β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
                β”‚  β”‚  term    β”‚ β”‚ pixels  β”‚  β”‚  two output paths
                β”‚  β”‚ (ANSI)   β”‚ β”‚ (RGBA)  β”‚  β”‚  same simulation
                β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
                β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
                      β”‚              β”‚
                      β–Ό              β–Ό
              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
              β”‚  lava-ssh    β”‚ β”‚  lava-wasm   β”‚
              β”‚  (russh)     β”‚ β”‚(wasm-bindgen)β”‚
              β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
                     β”‚                β”‚
                     β”‚                β–Ό
                     β”‚         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                     β”‚         β”‚   lava-web   β”‚  axum static server,
                     β”‚         β”‚              β”‚  embeds wasm bundle
                     β”‚         β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
                     β–Ό                β–Ό
                  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                  β”‚            lava            β”‚  single static binary,
                  β”‚ tokio::try_join!(ssh, web) β”‚  runs both servers
                  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

The browser runs the simulation client-side via WebAssembly. No WebSocket, no per-connection state, no rate limits β€” lava-web is pure static hosting and the page calls wasm.renderRgba() β†’ ctx.putImageData on every animation frame. The whole web bundle (HTML, JS, WASM) is include_bytes!'d into the binary.

Library usage

ANSI / terminal output:

use lava_engine::{Palette, Session};

let mut session = Session::new(80, 30, Palette::Bubblegum);
let mut frame = Vec::new();
loop {
    session.tick(1.0 / 30.0);
    frame.clear();
    session.render(&mut frame);
    // write `frame` bytes to a PTY, stdout, …
}

RGBA pixel output (same engine, different sink):

session.render_rgba(&mut frame);
// frame is `width * height * 4` bytes β€” feed to a canvas, PNG encoder, …

Layout

lava/
β”œβ”€β”€ crates/
β”‚   β”œβ”€β”€ lava/           single-binary entrypoint (ssh + web)
β”‚   β”œβ”€β”€ lava-engine/    simulation, palettes, term + pixels renderers, Session
β”‚   β”œβ”€β”€ lava-ssh/       SSH server library (russh)
β”‚   β”œβ”€β”€ lava-wasm/      wasm-bindgen wrapper exposing the canvas API
β”‚   └── lava-web/       axum static-asset server library
└── script/
    β”œβ”€β”€ setup           generate dev host key (idempotent)
    β”œβ”€β”€ build-wasm      wasm-pack build β†’ static bundle
    β”œβ”€β”€ server          setup + build-wasm + run unified binary
    └── test            cargo fmt --check + clippy + test

License

MIT

About

πŸŒ‹ ssh lava.watch

Resources

License

Stars

Watchers

Forks

Contributors