The auto-clips are now actual short-form video
Every finished game already became a vertical clip. They just all looked the same. Now each one opens on a hook, calls out the money, and ends on a card.

In plain words
opolyx turns finished games into vertical highlight clips and posts them automatically. That part has worked for a while. The problem was that watching three of them felt like watching one.
- about 40% of the frame was empty background around a centred board;
- they opened mid-game on an arbitrary dice roll — which is also the thumbnail;
- the payoff was unreadable: a 12,000 rent hit was one grey line in a small feed;
- every event got identical screen time, so the run-up was as long as the moment;
- they were silent, they had no ending, and the caption and hashtags never changed.
Each clip is now directed rather than dumped: it opens on a hook that tells you what you are about to see, the board fills the frame, the money and the big events are called out as they land, and it closes on a card telling you where to play. The pacing skims the bookkeeping and slows down for the moment that matters — including a brief freeze before a payoff you can already guess.
Captions, titles and hashtags now come from pools instead of a single line, and when one game produces several clips they are picked to be about different things rather than three views of the same bankruptcy.
For the technically curious
The renderer is a worker on a small VPS that loads the live site in headless Chromium and records the replay route. That is the leverage: the entire look of a clip is web code, so it ships with a normal deploy and needs nothing done to the worker.
The linchpin is recovering the anchor. The replay route only receives a range, not "the moment" — so the page re-runs the engine’s own highlight scoring over the full event log and takes the top-scoring point inside that window. That provably recovers the moment the worker picked, which is what let the visuals ship without touching the worker at all.
// lib/clip/pacing.ts — screen time is a function of what the event is
const BOOKKEEPING = 110; // turn_ended, dice_rolled: skim
const NORMAL = 320;
const ANCHOR_MIN = 1400; // the moment the clip exists for
const PREDICT_FREEZE = 600; // "what happens next?" before a guessable payoff
export function paceFor(event: DomainEvent, isAnchor: boolean): number {
if (isAnchor) return Math.max(ANCHOR_MIN, NORMAL);
return isBookkeeping(event) ? BOOKKEEPING : NORMAL;
}| Before | Now | |
|---|---|---|
| Structure | one continuous replay | hook, play, end card |
| Pacing | 700 ms × 65 events | 110–1400 ms by event weight |
| Length | ~45 s, always | 14–27 s, fitted |
| Post text | one caption, four fixed hashtags | seeded pools per clip |
| Audio | hardcoded off | game cues, optional music bed |
Selection changed too: when one game yields several candidate moments, the picker keeps the best of each distinct kind, so a single match stops producing three near-identical clips of the same bankruptcy.
