< back to work > case_study

> · featured

Sumo App

Real-time sumo tournament tracker — live SSE updates, command-palette navigation, hand-prompted sumi-e illustrations

Live basho standings, wrestler profiles, and a build-time pipeline of sumi-e ink illustrations — because if you're going to learn about sumo, you might as well do it properly.

Stack

  • Next.js 16
  • React 19
  • TypeScript
  • TailwindCSS
  • TanStack Query
  • SSE
  • FLUX.1
  • IP-Adapter
  • OpenAI gpt-image-1

Engagement

  • statuslive
  • modelside project
  • since2026
  • > solo, weekends

Sumo App started as “I want to follow a basho and the official sites are kind of awful,” and quickly turned into “what if the entire visual identity were generated from prompts.” It tracks live tournament standings (via sumo-api.com), wrestler rankings, day-by-day matches, and the 86 finishing techniques (kimarite) that decide them.

Stack is Next.js 16 + React 19 with TanStack Query doing the server-state caching and a thin SSE layer pushing webhook updates from the upstream API straight into the query cache (with HMAC signature verification, because that’s table stakes).

~/sumo-app

$ python scripts/stats.py

  • sumi-e illustrations generated 126
  • wrestlers indexed 191
  • lines of typescript 14 177
~/sumo-app

The AI bit (the actual reason this exists)

There’s no LLM call at runtime — every visual asset is pre-generated at build time by a small Python pipeline in /scripts. Two pipelines, both producing sumi-e ink-brush illustrations because that’s the only style that wouldn’t look ridiculous next to centuries-old sumo terminology.

Kimarite illustrations (86 techniques):

  • A 291-line hand-written prompt file, one entry per technique, describing the pose, contact point, and intent
  • A unified style suffix appended to all 86 prompts so the set holds together visually
  • Generated through FLUX.1 Dev locally (28 inference steps) or OpenAI gpt-image-1 as the cloud option
  • White background → transparent post-process so they composite cleanly into the UI
~/sumo-app/kimarite

Rikishi portraits (191 wrestlers):

  • Source photo from sumo.or.jp, watermark cropped
  • IP-Adapter v2 extracts facial identity from the source
  • Rank-conditional prompting: yokozuna get the tsuna rope and a katana; everyone else gets a kesho-mawashi (ceremonial apron)
  • Kanji ring-name rendered in calligraphy in the corner
  • --ip-scale knob trades likeness for artistic freedom — settled around 0.65 after a lot of A/B testing
~/sumo-app/portraits

The result is a growing custom illustration set that would have cost real money to commission, generated for the price of a few GPU hours and a lot of prompt iteration.

Other bits

  • Hybrid SSR + client-side TanStack Query — server fetches, hydrates initialData, client takes over with stale-while-revalidate
  • A cmdk command palette for jumping between tournaments, wrestlers, and techniques (because everything is named in romanized Japanese and nobody can spell anything correctly on the first try)
  • Real-time webhooks that invalidate exactly the right queries — matchResults invalidates ["matches", bashoId] and ["banzuke", bashoId], not the world
~/lighthouse
88
performance
100
accessibility
96
best practices
100
seo
run against production