Contributions are welcome: bug fixes, accessibility improvements, UX enhancements, and build tooling changes. This guide covers local setup, conventions, and the PR process.
Prerequisites: Deno v2+.
Clone with --recurse-submodules (or run git submodule update --init after
cloning) to populate
common-web/.
On the submodule:
common-webincludes web components shared by other Lodes & Lanterns projects. Torchtimer is self-contained and deployed on its own subdomain.
deno task dev
# open http://localhost:3000Starts an esbuild dev server at the project root. CSS and HTML changes take effect on refresh. TypeScript changes are bundled on demand with no rebuild step.
Bundles TypeScript, cache-busts assets, and assembles the staging/ deploy
payload:
deno task buildServe and browse the result:
cd staging
deno run --allow-net --allow-read jsr:@std/http/file-server --port 8000
# open http://localhost:8000Unit tests cover the pure-logic modules. Run them with:
deno task testNo flags required; the tested modules don't access the DOM, filesystem, or network.
| File | What it covers |
|---|---|
tests/timer.test.ts |
Timer lifecycle: create, light, add time, tick, pause, reset, death; round mode create, advance, add, convert |
tests/sync.test.ts |
serializeForSync and deserializeFromSync across all four timer states |
tests/share.test.ts |
buildFragment, decodeFragmentToData encoding/decoding, round-trips |
tests/flame.test.ts |
flameParams, glowParams, computeWind, computeHeadBg |
tests/sway.test.ts |
computeTorchSway: determinism, amplitude, seed independence, bounds |
tests/noise.test.ts |
valueNoise and fbm: range, determinism, octave stacking |
tests/smokegl.test.ts |
buildSmokeInstances: empty pool, particle layout, instance count |
tests/cracks.test.ts |
generateCracks: structure, determinism |
tests/qr.test.ts |
getQrModules: module grid dimensions, content variation |
tests/utils.test.ts |
formatTime, nextTorchNumber, and other pure utility functions |
The DOM-manipulating and WebGL setup code in render.ts, dom.ts,
torches.ts, overlay.ts, live.ts, viewer.ts, audio.ts, and main.ts
requires a browser environment and is not covered.
When to add tests: any new pure function (no DOM, no network, deterministic output) should have unit tests. When fixing a bug in a pure function, add a regression test that would have caught it before adding the fix. Don't write tests for coverage alone; write them to document a real behavior or guard a real failure mode.
- No framework. The frontend is plain TypeScript with native ES modules and a thin esbuild bundling step.
- No CSS framework. All styles live in
css/andcommon-web/using custom properties. - Minimal dependencies. Torchtimer aims for as few dependencies as reasonably possible.
- Pure functions, please. Extract pure logic out of modules without DOM dependencies so they can be unit tested. Side-effectful code (rendering, WebGL, event handlers) is expected to be manually tested.
- Module structure.
src/main.tsis the entry point (wiring + boot only). Logic lives in:src/timer.ts: immutable timer state machine (unlit/running/paused/dead) for both time-based and round-based modessrc/sync.ts: live-sync serialization/deserializationsrc/share.ts: URL fragment encoding and decodingsrc/types.ts: shared TypeScript types and interfacessrc/state.ts: global application state singletons and shared constantssrc/utils.ts: pure math, color, and formatting utilitiessrc/noise.ts: 2D value noise and fractal Brownian motionsrc/flame.ts: particle system parameter computation for flame, smoke, and sparkssrc/sway.ts: per-torch body sway computationsrc/glow.ts: canvas and DOM glow effects for lit torchessrc/cracks.ts: procedural glowing crack lines and specks on the torch headsrc/audio.ts: procedural per-torch audio (flame and popping sounds)src/render.ts: per-frame render loopsrc/dom.ts: DOM manipulation for torch elementssrc/torches.ts: torch lifecycle and interactionsrc/duration.ts: Pass Time/Rounds, Add Time/Rounds, custom duration modal, and mode toggle logicsrc/live.ts: Torchbearer live-hosting UI and session managementsrc/viewer.ts: viewer mode entry, exit, and poll-driven state applicationsrc/overlay.ts: canvas overlay initialisation and resize handlingsrc/smokegl.ts: WebGL2 instanced smoke particle renderer
- Build script is build-time only.
build.tshandles bundling and cache-busting. No runtime logic belongs there.
Deployment is fully automated via GitHub Actions
(.github/workflows/deploy.yml).
On every push to main, the workflow runs tests, builds to staging/, and
force-pushes that directory to the gh-pages branch. GitHub Pages serves that
branch at torchtimer.lodesandlanterns.com. After a successful deploy, the
workflow tags the commit with the current UTC date in CalVer format
(YYYY.MM.DD), appending a numeric patch suffix if multiple releases land on
the same day (2026.03.28 -> 2026.03.28.1, etc.).
PRs to main run tests automatically via .github/workflows/test.yml.
The live sync backend,
torchtimer-service,
is a Cloudflare Worker deployed separately using
Wrangler.
Open an issue in this repository for bugs, accessibility problems, or feature requests.
- Fork the repository and create a branch.
- Make your changes. Test locally using the dev workflow above.
- Open a PR against
mainwith a clear description of what changed and why. - CI runs tests automatically. A failing test run blocks merge.
Keep PRs focused. A bug fix and an unrelated refactor are two separate PRs. If you're unsure whether a larger change is the right direction, open an issue to discuss first.