Scratch
A single Flask app that hosts a growing collection of small, self-contained web experiments.
If you have an interesting thought, or you get nerd sniped, you should have somewhere to explore your ideas. That’s what Scratch is for. It holds small web experiments, each one starting with some inconsequential question.
Scratch is available here. All of the architecture and development notes are available here.
A single Flask app deployed as a Podman container serves all the experiments. Frontends and backends are logically separated in the code base. Each experiment’s UI is its own directory with its own tooling, while every API shares one Python environment. Adding a new experiment is as simple as dropping in a frontend and registering a route.
| App | Description |
|---|---|
| Gazette | A newspaper themed front page for hosting standalone HTML files, each with its own listing. |
| Guestbook | A tiny public guestbook backed by Postgres on the same VPS as this blog. |
| Joust | A no-login tournament coordinator that builds a bracket and tracks scores until it crowns a champion. |
| Settlers React | An interactive Settlers of Catan UI built to demonstrate optimistic updates and responsive SVG layouts. |
| Token Bucket | An interactive visualization of the token bucket algorithm for rate limiting. |
Gazette
Host small, standalone HTML files that can be opened from anywhere. The newspaper theme treats each HTML file as an address or “listing.”
A small sort control displays the HTML files by “listing date” or alphabetical order. To add a new listing, just drop an HTML file into the repo and update the index page.
Guestbook
The first stateful Scratch application. Mostly a pilot for how a more complicated stateful application could be deployed. A simple message board backed by a real Postgres database on the same VPS that hosts this blog.
Anyone can leave a note up to 280 characters. The React frontend posts to a Flask API, which validates input, deduplicates identical messages submitted within 60 seconds, and inserts into a entries table. A row-cap trigger keeps the table bounded at 10,000 rows. Rate limiting is layered at two levels: nginx enforces a per-IP request cap, and the Flask route adds a second check before hitting the database.
Joust
A no-login tournament coordinator for friends. You create a tournament, share a PIN, let everyone join teams, and lock the lobby. Joust then builds a seeded bracket that you play through by reporting scores until it crowns a champion.
There are no accounts. Each device gets a token saved in localStorage, and anyone with the tournament’s PIN can join and act. Players set a confidence level when they join a team, and those numbers feed the seeding. Joust supports single elimination, double elimination, and round robin into a knockout. Each matchup also shows American moneyline odds that start from team confidence and then mix in each team’s record so far as the bracket plays out.
The seeding, odds, and standings are plain functions with no database access, so they are easy to test on their own. The Flask backend stays thin. A blueprint reads the request, a service layer runs the logic, a repository owns the SQL, and Pydantic models carry the data. State lives in Postgres under a limited role that can only run SELECT, INSERT, and UPDATE queries. Nothing is ever hard deleted, so empty teams disband and removed players are flagged instead of erased. The frontend is a React SPA built with Vite and Mantine, written in TypeScript whose types match the API responses.
Settlers React
An interactive Settlers of Catan UI that demonstrates optimistic updates and responsive SVG layouts.
This thought experiment / demo was inspired by the Catan web app I’ve been working on sporadically for several years.
The board is rendered entirely with SVG. Each hexagonal tile, edge, vertex, and port indicator is a React component positioned by a gridDataBuilder utility that computes SVG coordinates from a declarative board definition. The viewBox is calculated dynamically from the outermost tile vertices, so the grid scales to any screen size with no pixel math inside the components.
The demo focuses on the “road placement” aspect of Catan. Clicking an available edge immediately renders the road in a pending color (an optimistic update) then fires a mock API call that succeeds or fails with equal probability. On success, the road snaps to its final color. On failure, the board reverts to its original state and the activity log records the rollback. Real game clients handle network latency the same way, which avoids freezing the UI.
Token Bucket
A simple interactive visualization of the token bucket algorithm for rate limiting.
The token bucket is a classic rate limiting strategy: a bucket refills with tokens at a fixed rate, and each incoming request consumes one or more tokens. If the bucket runs dry, requests are rejected until enough tokens accumulate. It’s the same algorithm behind rate limiters in systems like DynamoDB’s RCU/WCU model.
The demo lets you configure four parameters in real time:
- Request pattern: Slow and steady, bursts, slow and steady with bursts, or heavy and steady.
- Bucket capacity: How many tokens the bucket can hold.
- Refill rate: Tokens added per timestep.
- Token cost per request: Tokens consumed by each request.
Animated particles show requests flowing down to the bucket and refills flowing up from below, with accepted/rejected/overflow feedback shown inline. A live statistics table tracks the last 100 requests.