tiledle.com is a Wordle-style word game that doesn't stick to five letters: each puzzle is a 4 to 8 letter word, with more attempts for longer words (five tries for four letters, up to nine for eight). The whole game, including the DOM handling, is written in Rust and compiled to WebAssembly; the only JavaScript is a few dozen lines to initialise the module.
How it plays
- A random length and word each game, with hundreds of words per length.
- Keyboard or on-screen input, green / orange / grey feedback, bonus tile animations.
- Every puzzle has a shareable id (an emoji string) so a friend can play exactly the one you did, and a daily puzzle on top of the random ones.
- Timer and score, and a share card with the classic coloured-square grid.
Architecture
It's a Cargo workspace of four crates: tiledle-wasm (game state, DOM updates, animations, the HTTP client), tiledle-server (a warp + tokio backend that hands out puzzles and records results), tiledle-api (the types shared by both ends, so a request can't drift from its handler) and tiledle-word-encoder (the puzzle id scheme). Styling is SCSS compiled by the Rust-based grass, so there is no npm in the build at all. Tracing goes out over OpenTelemetry to a Grafana dashboard.
tiledle/
tiledle-wasm/ src/app.rs, game.rs, dom.rs, api.rs, animation.rs
tiledle-server/ warp routes, puzzle store, OTLP tracing
tiledle-api/ shared request/response types
tiledle-word-encoder/
web/ index.html, scss/, js/app.js (< 100 lines)
Running it for pennies
The site runs on Google Cloud as a single preemptible spot VM inside a managed instance group of size one. If the spot instance is reclaimed the MIG recreates it, and a startup script grabs the reserved static IP so DNS never changes. Deploys build a Docker image, push it to Artifact Registry, stamp a new instance template and roll the group over: zero-downtime updates on a machine that costs a few pounds a month. Caddy handles HTTPS.
Why Rust in the browser
The first version was plain JavaScript. Moving the logic into Rust meant the server and client share one set of types, the game state can't get into an impossible shape, and the WASM bundle is small. The old JS lives on in an archive folder as a reminder of how much easier it is to refactor with a compiler shouting at you.