A hidden Markov model catches a dealer swapping in a loaded die, roll by roll.
A dealer mostly rolls a fair die but sometimes switches to a loaded one that lands on six half the time, and a hidden Markov model works out which die was in play from the rolls alone. The Viterbi algorithm runs incrementally, keeping the best log probability and a back-pointer for each state at each roll: those back-pointers form the trellis, and tracing back from both newest stations shows the two surviving paths, which merge at a junction left of which the decoding is settled forever while every unused edge fades and dies. The winning path is drawn as a railway line with sleepers. Above the dice, scaled forward-backward over a sliding window gives the soft answer, P(loaded | all the rolls), as a two-color ribbon that keeps revising the past as new rolls arrive.
Try it. Roll the die yourself with the face buttons or keys 1 to 6 (try a run of sixes). Switch the dealer between honest, sneaky and crooked, hover a column to read its posterior and decoding, and use plus and minus to change the rolling speed.
Paste this into Claude Code, Codex or any coding agent to get a simple version running, then take it wherever you like.
Build a visualization of the Viterbi algorithm on the classic dishonest casino hidden Markov model with JavaScript and the HTML canvas element. Put everything in a single index.html file with no libraries or build step, so I can open it directly in a browser.
Start simple:
- Make a canvas that fills the window, stays sharp on high-DPI screens (scale by devicePixelRatio), and resizes with the window.
- Model two hidden states: a fair die (each face 1/6) and a loaded die (six with probability 1/2, other faces 1/10). Transitions: fair stays fair 0.95, loaded stays loaded 0.9.
- Simulate a dealer who follows that chain and rolls a die every few hundred milliseconds. Keep the rolls and the true states.
- Run Viterbi incrementally in log space: for each new roll and each state, keep the best score of any path ending there plus a back-pointer to the previous state. Subtract the max score each step so numbers never underflow.
- Draw the rolls as small dice in a row, two rows of nodes below them for the fair and loaded states, the back-pointer edges between columns, and the best path traced back from the newest column in a bold color.
Once that works, make it beautiful:
- Trace back from both newest nodes. Where the two paths meet, everything to the left is final: mark that junction and fade out edges no surviving path uses.
- Draw the decoded path like a railway line with sleepers on a dark background, and scroll smoothly as new rolls arrive.
- Add forward-backward with per-step scaling over the last 100 or so rolls and draw P(loaded) as a soft two-color ribbon above the dice, plus strips comparing the decoding with the truth.
Explain the key ideas in short code comments. When you're done, tell me how to open it and suggest three directions I could take it next, such as buttons to roll the die myself, learning the transition probabilities with Baum-Welch, or a three-state model with a second loaded die.