Two movie posters, one question: which one do you keep? 🎬
Two posters show up. Swipe towards the one you like more — it stays on screen and takes on the next challenger. Keep going until the deck runs out, and the last poster standing is your champion. At the end you get the full list of everything you picked along the way.
Swiping works with a finger, a mouse, or the ← / → keys, and a tap on a poster picks it too.
- 202 films — 119 Disney, 52 DreamWorks, 31 Pixar.
- Pick your studios — one toggle each. Sequels (49 films) and live-action (56) cut across all three, so they are switches of their own: a live-action remake is still a film by the studio that made it, not a studio of its own.
- Animation is the complete canon — all 64 Walt Disney Animation Studios features, all 31 Pixar, 51 of the 53 DreamWorks Animation ones. Live-action is a curated pick of what a child would recognise, not the 535 films Walt Disney Pictures has released.
- Pick your length — a short 12-poster run, a 24 one, or the whole catalogue.
- Fully offline & installable — a PWA that keeps working with no connection after the first load, posters included.
- 6 languages — English, Español, Euskara, Galego, Català, Valencià.
- No accounts, no backend, no ads — nothing leaves your device.
Vanilla TypeScript + Vite, Tailwind for the UI, Vitest for the tests.
npm install
npm run devThe catalogue is generated once and committed, not fetched at runtime:
npm run fetch:catalog # tools/catalog-seed.json -> src/data/catalog.json + public/posterstools/catalog-seed.json is the curated part — which films, which category, which are
sequels, and any hand-written title. Everything else (localized titles, poster image) comes
from Wikipedia and is cached under tools/.cache, so a rerun makes no requests and produces
a byte-identical result.
Ports and adapters, with dependencies pointing inwards only:
src/domain/ the game itself — deck, king-of-the-hill match, options, and the ports
it needs (FilmCatalogue, OptionsStore, Translations, Random).
Imports nothing else. No document, no window, no fetch.
src/adapters/ one implementation per port: the bundled catalogue, web storage,
the locale files, Math.random.
src/ui/ the screens. They receive a Deps object; they never import an adapter.
src/main.ts the composition root — the only file that picks implementations.
Injection is by parameter, not by container: at this size a container would be ceremony.
The payoff is in the tests — tests/support/fakes.ts swaps in an in-memory options store, a
two-film catalogue and a seeded random, so nothing is mocked. tests/architecture.test.ts
fails the build if any of those arrows ever turn around.
Movie posters are the property of their respective studios and are used here as low-resolution thumbnails. See NOTICE.