Shake the ball. A neural network living inside a toy tells your fortune.

Overview

LLM Magic 8 Ball is a Mattel Magic 8 Ball, dissected and rebuilt around a Seeed Studio XIAO microcontroller and a round LCD, running a tiny language model that writes a brand-new "hacker horoscope" every time you shake it. It types out on screen where the little blue d20 used to float.

No cloud, no API keys. Every fortune is generated one token at a time, by a transformer small enough to fit inside a toy, trained on a desktop PC, then squeezed onto a ~$20 microcontroller.

LLM Magic 8 Ball diagnostics screen, showing the on-device model self-test

"Signs point to yes, the connection pool is down." - the actual on-device model

Why bother with an LLM this small?

A ~0.33 million-parameter model running on a 64 MHz MCU with 256 KB of RAM produces fortunes that are grammatical-ish, thematic-ish, and occasionally unhinged, which is roughly correct behavior for a Magic 8 Ball. It writes the horoscope from scratch every time, instead of picking from a fixed list.

At this size, coherence is the limit. It stays on the rails for a few words and wanders over longer sentences. So the ball also has a second, pre-generated mode (a random pick from ~180 curated fortunes) selectable from the diagnostics page, for when you want a consistently clean answer. The LLM mode is the default.

How it works

   DESKTOP (one-time)                     INSIDE THE BALL
   ─────────────────                      ───────────────
   Claude (via Claude Code) writes        shake -> IMU wakes the nRF52840
   short hacker horoscopes                         |
        |  train a tiny BPE transformer             v
        v  quantize to int8                 m8b runs inference, int8 weights
   model.bin (~328 KB), embedded in         read in place from internal flash,
   firmware flash (bin2c -> .h)             dequantized on the fly
                                                    |
                                                    v
                                            fortune types onto the round LCD

Status

The software stack works end-to-end on real hardware. The physical build is what's left.

  • On-device LLM generating live. A memory-efficient int8 transformer (m8b) runs the forward pass on the MCU, weights held in internal flash and dequantized on the fly. Verified against PyTorch before every flash.
  • Two selectable modes. LLM (default) and pre-generated, toggled by a button on the diagnostics page and remembered across power-offs.
  • Oracle UI on the round display. Idle 8-ball with a lifetime counter persisted in flash, a spinning-ball loading ring, the fortune typing out in phosphor-green, then a 60-second hold. Shake detection via the onboard IMU, plus a touch-activated, scrollable debug screen with a QR code, as seen in the photo above.
  • Training pipeline. A short-fortune hacker-horoscope corpus generated by parallel Claude Code subagents (no API key), a BPE subword transformer trained on it, exported to a ~328 KB int8 blob and embedded in the firmware.
  • Next up: deep sleep and shake-to-wake, a low-battery indicator, then dissecting the ball and assembling it. Stretch goal is a ~10x bigger model in the 2 MB QSPI flash.

Hardware

Part ~Price
Seeed XIAO nRF52840 Sense Plus (onboard IMU, LiPo charger) ~$16
Round Display for XIAO (1.28" 240x240 GC9A01, touch) ~$18
Mattel Magic 8 Ball + a 3.7 V LiPo (JST 1.25) ~$23

AI transparency

Planned, documented, and largely coded with Claude (Anthropic's Fable 5 and Opus 4.8) via Claude Code, so the repo doubles as a test of that workflow. The training corpus is generated by Claude through the Claude Code CLI (no API key). The fortunes come from the tiny on-device model, or in pre-generated mode, from that same Claude-written corpus. The full prompt history is published in the repo's prompt.txt.