Skip to content

~/wayyy-out $ cat for-nerds.md▍

For the nerds.

How we build our games, what keeps them running, and the bits we have given away. No marketing, just the interesting parts.

Interrogated!

A language model in every suspect. No server in sight.

You talk, they talk back, and every step happens on your own computer. Three open models hand off to each other in a loop that never touches the internet once the models are downloaded.

  1. YouAsk out loudHold the mic, ask anything
  2. Moonshine TinySpeech to textA tiny speech model on LiteRT
  3. Gemma 4Thinks in characterLiteRT-LM on your GPU, CPU fallback
  4. KokoroText to speechA voice per suspect via sherpa-onnx
  5. SuspectAnswers backWith a face to match the mood

The mystery is code, not the model

Before anyone says a word, a seeded generator builds the whole case: one victim, one killer, two innocent liars with false alibis and secrets, a witness, a framed suspect and the companion who can clear them. Same seed, same case, every time.

Each suspect's prompt only holds the facts they are allowed to give away, and when. The model plays the part; it never decides who did it, so a case always adds up.

3.66 GB Gemma 4 E4B, downloaded once, then fully offline.
32 Steam achievements, all triggered by game state and never by what the model happens to say.
A reply, as the model writes it
[suspicious] I was in the conservatory all evening, Inspector. // ← the mood tag picks the portrait
Ask the botanist if you don't believe me.            // ← a lie the generator planted
About the game

The mobile games

Flutter on the phone. Firebase everywhere else.

Shuffle, Ludo, Trivia and Runner share a backbone. Players start without signing up, games sync live, and the rules that matter run on the server where nobody can tamper with them.

Flutter appRiverpod or Provider, go_router, and plenty of flutter_animate
Firebase
  • AuthenticationPlay straight away, save with Google later
  • Cloud FirestoreProfiles, friends, leaderboards, word banks
  • Realtime DatabaseEvery roll of a live Ludo game
  • Cloud FunctionsTypeScript on Node 22, inputs checked by zod
  • Remote ConfigTune the games without shipping an update
  • App CheckOnly our real apps get through
  • Crashlytics & AnalyticsWhat broke, and what is fun
  • Cloud MessagingGame invites and leaderboard alerts

Words that never repeat

Simple Shuffle serves puzzles from a reviewed word bank built offline. Each player walks their language and difficulty bucket in their own seeded order, so a word only comes back once the bucket runs dry. No AI and no third-party APIs at runtime.

Moves that can't arrive late

Online Ludo runs on the Realtime Database. Every game state carries a sequence number, so a move made against a stale board is simply rejected, and everyone stays on the same turn.

Simple Sudoku

A daily puzzle, and no server.

Simple Sudoku never goes online. Puzzles are carved out of random grids on the phone, in about a millisecond each, and kept only if they have exactly one solution. A solver then grades each one by the hardest technique it needs, which is what decides Easy from Expert.

The daily puzzle is seeded from the date, so everyone gets the same board without asking anyone. Any puzzle can be shared as a level letter plus its seed in Crockford base 32, which leaves out I, L, O and U so codes survive being read aloud.

Code
N4F7-K2QX
Level
Normal
Play this puzzle

Open source

Bits of our games, free on pub.dev.

Dart package · MIT

ludo_engine

The rules and computer players behind Simple Ludo, in pure Dart with no dependencies. Immutable and deterministic, with twelve house rules and computer players that turn vicious when you keep kicking them. Runs in an app, on a server or in a test.

final result = Engine.apply(state, AiPlayer.decide(state, rng), rng);
for (final e in result.events) {
  if (e is Captured) print('Sent home!');
}
Flutter package · MIT

open_peeps_avatar

The avatar system in Simple Shuffle, Trivia and Ludo. Players mix heads, faces, outfits, poses, skin tones and colours; the whole look packs into a single number, and a ready-made editor drops into any Flutter game.

final avatar = const AvatarLook(head: 0, face: 0, facialHair: 0, accessory: 0)
    .shuffled(Random())
    .encode(); // the whole look, in one int

PeepSticker(avatar: avatar, size: 48, animate: true);

This website

Yes, we nerded out here too.

  • Angular, prerendered. Every page is built to static HTML at deploy time, so it loads fast and link previews work without JavaScript.

  • GSAP and Lenis. ScrollTrigger and SplitText drive the motion, wired up through plain data attributes. With reduced motion switched on, all of it steps aside.

  • Tailwind CSS. Each game page is the same template, recoloured with a handful of CSS variables to match the game.

  • Vercel. Static hosting, plus one small function that writes a shared Sudoku puzzle's level into its link preview, because chat apps don't run JavaScript.