---
title: "ALTXI"
url: https://sharadbapat.com/experiments/altxi/
description: "Cricket between people you know, played out by a seeded simulator. The result is fixed before the first ball, and your browser can check it."
published: 2026-08-23
author: Sharad Bapat
---

# ALTXI

Type in your friends' names, five a side, and watch a cricket match that never happened, ball by ball, with commentary. A random seed chosen before the first ball decides everything, and when the match ends your browser can prove the seed wasn't swapped.

[Open ALTXI](https://altxi.sharadbapat.com/)

- **Made:** Aug 2026
- **Built with:** TypeScript, React, Hono, Cloudflare Workers and D1
- **Status:** Live

## The question

Can a game be fair without anyone having to trust the server? In ALTXI nobody controls anything: no tactics, no ratings, no buttons during play. The only way to cheat would be to change the result after the fact, so the whole design is about making that checkable.

## How it works

- **Two ways in.** A quick match is two five-player teams set up on one phone, or a challenge link so the other side picks their own team. A league is 4 to 8 teams of 5 to 11 players, playing one round-robin season.
- **Everyone is equal.** There are no player ratings. The tests check that a player's position in the list makes no difference.
- **One seed decides everything.** Each fixture stores a random 256-bit seed. Separate deterministic streams (xoshiro128**) drive the play, the line-ups and the commentary, so the same seed always produces the same match, byte for byte.
- **Revealed ball by ball.** The Worker simulates the whole match up front but only sends what has happened so far. Until the end, no response contains the seed, a future ball, the final scores or the winner.
- **Commentary in English or Hinglish.** A six might get "ne ball seedha WhatsApp group mein bhej di": he sent the ball straight into the WhatsApp group.
- **Proper cricket, short.** Matches are 5 or 10 overs. Ties go to a Super Over, and league tables use net run rate.

## Checking the result

Before the first ball, each match publishes a commitment: a SHA-256 hash of the seed, the fixture and the rules version. The hash gives nothing away about the seed. When the match ends, the seed itself is published, and your browser recomputes the hash. If they match, the seed, and so every ball, was fixed before the match began.

```
commitment = sha256(seed + fixtureId + rulesVersion)
```

This is commit-reveal, the same idea as sealing your guess in an envelope before the answer is known.

## Evidence

- 20 tests: fixed random-number vectors, byte-for-byte repeatability, engine invariants across seeded samples, equality across roster positions, tied matches and Super Overs, round-robin fixture counts, and net run rate when a side is all out.
- About 3,900 lines of TypeScript. One Worker and one D1 database: no accounts, no background jobs, no WebSockets and no AI at run time.
- In September it moved to its own address with a fresh database and no analytics. Checked after the move: pages and API responses serve with no injected scripts, and a new match is created with its commitment in place and its seed hidden.

## What didn't work, and the trade-offs

- **Commit-reveal proves less than it seems.** It shows the seed wasn't changed after the commitment. It doesn't show the seed was chosen fairly: the server picks it, and could in principle try many seeds before committing to one it liked. Nothing here guards against that except that the server has no stake in the result. The fix would be to mix in randomness from the players themselves.
- **Real names are personal data.** Matches are public only after an explicit acknowledgement, and can be switched to link-only or deleted. Public pages take reports, and a league can be hidden while a report is reviewed by hand.
- **The move started from scratch.** Matches played at the first address didn't come across.
- **No live push.** Without WebSockets, a match page asks for new balls as the match plays out, rather than being sent them.
