Design direction · for Justin Butlion's "Commit Town" · 17 Aug 2026

Your GitHub is a town.
Make it alive, not just rendered.

Design board · JanardhanVisualisation: Higgsfield (nano_banana_pro / seedream 4.5 / kling / seedance)Status: direction, not spec
Hero: a low-poly clay diorama of a small town on a plinth over water — train loop, crane, balloons, park
Hero plate — Stage 3 "Town", golden hour. Everything on this board derives from this one image so the whole set reads as one world.
01 · What I think

Good instinct, wrong hard part.

The idea is charming and the passive loop is genuinely the right shape for developers (the work is work you were doing anyway). But the "GitHub → 3D city" screenshot is already a commodity, and the living part — the thing Justin called the hard part — is the cheap part. What is hard is giving anyone a reason to come back.

The category is crowded — and one entrant already found the ceiling

FEB 2026 · 5.8k★

Git City (srizzon)

One shared metropolis; every profile is a skyscraper (height = contributions). Windows glow while you code, VS Code ext, shop, share cards, referrals. Grew 211 → 11k signups/day after one Reel. $1,606 all-time revenue, no subscriptions. Planning "visual decay" + multiplayer.

repo · TrustMRR

2023 · 1.3k★

GithubCity (honzaap)

Per-user low-poly Three.js diorama from one year's grid, boats and clouds loop, STL export. PH #2 of the day. Dormant since Oct 2023. Ships 111 GLBs ≈ 6 MB and real-time PCF shadows — the perf trap to avoid.

repo

2026 · SVG EMBED

GitCity · GitAnimals · Spaceorbust

GitCity: 365-day iso skyline with a README SVG embed; got "clone" accusations on launch day. GitAnimals (1.8k★): commits → animated pets in your README with a points economy. Spaceorbust: typed event→resource civ game — text only, 1★.

GitCity · GitAnimals · Spaceorbust

Also GitHub's own Skyline (now a CLI, STL export), contribution-city, gitown, gitcity.io, isometric-contributions (3.7k★). Nobody has shipped simulation: state that evolves between visits, allocation you steer, commit ≠ merged PR ≠ review ≠ star rendered as different things in the world, time as an axis. That residue is real. It just has to be visible in the first screenshot or the internet files it under "another GitCity".

My position, in eleven lines

  1. The share moment is not the picture. Static iso screenshots are commodity. Motion and evolution are the hook — a loop, a timelapse, "your town just became a City".
  2. Justin has the hard part backwards. Rendering is cheap; kit authoring is medium; the return visit is hard.
  3. The city is the dashboard. Kill the stat tiles as primary UI. If the number isn't visible in the town, the town isn't doing its job.
  4. Alive = 6–8 cheap ambient layers + the developer's real clock + consequence. Not fidelity.
  5. Never punish. Dormancy is weather (fog, parked cranes); return is sunrise. GitHub removed streaks for a reason.
  6. Diorama on a plinth. The town becomes an object — that is what people post — and one composition works as OG image, README banner and 9:16 clip.
  7. Low-poly clay, value-first. Justin's own reference, and the anti-Git-City look. Reject RPG-Maker sprites: asset-flip, and it's what the category already looks like.
  8. Weight merged PRs, reviews and stars over commits. Raw commits are gameable (gitfiti) and punish the senior dev on a private monorepo.
  9. Never ask for repo scope. There is no read-only private scope; use read:user + GraphQL contribution counts.
  10. Ads are not a business. Dev CPMs are $0.50–2.50; 100k views ≈ $50–250/mo. Money, if any: a Mayor tier (private counts + cosmetics) and org towns.
  11. Ship the README embed first, then the public URL with a live OG image, then event-triggered clips. That is the distribution; X is the lottery.
Verdict: GET-DATA — leaning "marketing toy" as a standalone product, real as a design object.
Kill criterion: if D7 return of people who shared is under 10%, or fewer than 30% of first-week visitors say it looks different from Git City, it is a share card, not a game — hand it to Justin as a ProjectEcho marketing toy. Smallest test (≤1 week, no OAuth, no economy): username → public counts → deterministic stage → one good render → OG card → post once, measure share-rate and D7.
Sources for every number on this page are linked inline; nothing here is from memory.
02 · Three design theses

What "living" actually means at 400 px.

THESIS A

The city is the dashboard

Population → house count and tiny walkers. Happiness → parks, lit windows, festival lanterns. Tourism → balloons, boats, a viewing tower. Technology → cranes, antennas, height. Neglect → fog, dim lights, parked cranes. Numbers are a secondary layer you can hide. Justin's prototype has this inverted: the tiles carry the information and the field carries none.

THESIS B

Alive = motion + time + consequence

Six to eight cheap ambient loops (train, crane sway, balloons, boat bob, windmill, flags, walkers, a blinking window). Day/night from the developer's own clock — a 2 a.m. push lights the town, and that is the shareable frame. GitHub events as visible beats: commit = bricks arrive; merged PR = topping-out; release = fireworks; star = a balloon lands; CI red = smoke. Growth is construction, never pop-in.

THESIS C

Diorama on a plinth, fixed iso, value-first

A visible plinth edge turns the town into an object; it grows and annexes islets with stage, solves the world edge, and frames identically for OG, banner and vertical clip. Fixed orthographic camera with four snap rotations and three zooms. Greyscale-check every asset (Dorfromantik), no dirt or noise (Two Point). Real-time shadow maps are banned — bake AO and warm ground bounce.

03 · Style direction

Three candidates, one pick.

All three were generated from the same brief. The differences are exactly the differences that matter at share size.

Low-poly clay diorama
PICKLow-poly clay diorama. Value-first, soft baked light, reads as a premium object. Justin's own reference. Cheapest to keep coherent as a tiered kit; vertex colours make themes and seasons free.
Voxel diorama
FALLBACKVoxel. "One commit = one block" is an honest metaphor and MagicaVoxel kits are cheap and coherent. Loses the softness; risks reading as Minecraft/Git City-adjacent at small sizes.
Storybook painted diorama
FOR COMPARISONStorybook / hand-painted. Lovely as a still, expensive as a kit (every new piece is an illustration), and hard to keep consistent across growth stages and user themes.

Rejected outright: pixel art / RPG-Maker sprites on a flat field. It is the asset-flip tell, it is what the category already looks like (Git City is pixel-neon), and it cannot make the plinth-object composition that the share unit needs.

04 · The living layer

Six to eight cheap layers beat one expensive one.

Ambient loop of the hero plate — camera locked, only the cheap layers move: train, crane, balloons, boat, windmill, walkers, one blinking window. This is the whole "alive" budget.

Ambient layers, priced

cheap Always on

  • Vehicles / a train on a spline loop
  • Boats bobbing (sine on Y)
  • Windmill, turbines, ferris wheel (rotation)
  • Vertex-shader wind on trees and flags
  • Blinking emissive windows and beacons
  • Balloons drifting on long, slow paths
  • One bird-flock clip (≈200 KB, Windland)

medium Worth it

  • Chimney smoke billboards
  • Water wobble shader
  • Rain / snow overlay by season
  • Crane swing + scaffold rising
  • Day/night lerp of sun, sky, hemisphere + emissives

expensive Not now

  • Skeletal pedestrian crowds
  • Moving-sun real-time shadows
  • SSAO / bloom / depth-of-field post (Windland FPS-gates post at 30 fps)
  • Physics of any kind

Beats — GitHub events made visible

Topping-out ceremony
PR merged → topping-out. Crane lowers the roof, windows light row by row, ribbon and confetti, a tiny inspector with a clipboard. Size ∝ lines changed (capped).
Release fireworks at night
Release → fireworks + a named landmark. Also the day/night proof: same geometry, night palette, emissive windows. Attach the share prompt to this moment.
Tourist balloon landing
Star received → a tourist balloon lands. Visitors are external validation and are weighted above commits. Tiers unlock a viewing tower, then an observation deck.
Dormant town in fog
Two quiet weeks → fog. Cranes park, windows dim, nothing is destroyed. First push back = sunrise sweeping the fog off. Dormancy is weather, not ruin.
CI red: smoke from the power plant
CI red on the default branch → smoke, one district flickers. A palette shift only. Clears on the next green. Honest, legible, non-destructive.
Season sheet
Seasons and streak festivals are palette + emissive swaps on identical geometry — free once the kit is vertex-coloured (Dorfromantik's trick).
05 · Growth stages

Every stage changes three things at once.

Max height, ground material (dirt → cobble → asphalt) and one hero landmark (well → clock tower → crane → towers → spire). Monotonic — a town never de-ranks. Thresholds borrow GitHub's own tier ladders (Pull Shark 2 / 16 / 128 / 1024, Starstruck 16 / 128 / 512).

Hamlet
1 · Hamlet
first login
Village
2 · Village
~2 merged PRs
Town
3 · Town
~16 merged PRs
City
4 · City
~128 merged PRs / 16★ repo
Metropolis
5 · Metropolis
~1,024 merged PRs / 512★
Timelapse Village → City (start and end frames from the strip). The "your town became a City" moment is the strongest share unit after the loop.
StageThreshold (civic points)Silhouette
Hamletfirst loginFlat warm island: 3–6 low cottages, one dirt road, a well, staked-out plots with survey flags, a parked cart. Wide low horizon.
Village~100 pts / 2 merged PRsFirst vertical: a stone church or clock tower. Cobbled market square with awnings, first balloon pad, windmill turning at the edge.
Town~750 pts / 16 merged PRsGrid roads, 3–5-storey blocks, small station with a looping train, town hall with flag, first park with pond, one crane. Skyline gets a shoulder.
City~4,000 pts / 128 merged PRsTwo or three glass towers, elevated rail loop, river with bridge, districts by repo, night mode, several balloons; plinth annexes an islet.
Metropolis~20,000 pts / 1,024 merged PRsOne supertall spire, harbour with cranes and ferries, tiny airstrip, monorail, monument park recording releases. Dense but readable, plinth edge still visible.

Points: merged PR 10 · star 5 · release 25 · review 3 · closed issue 2 · commit 1 (daily cap). Overlays that must read at 400 px: fog (dormant), lanterns/fireworks (festival), crane + scaffold (construction).

06 · Commits are the bricks

Your contribution graph is the ground plan.

53 weeks × 7 days is already an isometric grid: 53 blocks by 7 avenues. Cell intensity becomes the plot — meadow, cottage, house, mid-rise, tower. Left to right is the town's history laid out in space. Your own graph — ten quiet months, then a June–August boom — renders as meadows in the west and a dense new quarter in the east: hamlet to town in one quarter, on real data.

Live: canvas, no libraries, ≈250 lines. Scrub or let it play. Reconstructed from your screenshot (344 contributions) — the public API for the three local logins totals 3 / 1 / 0, so the 344 view is a private-inclusive view of another account; swap in contrib.json from GraphQL contributionsCollection when you want the exact cells.

Your graph rendered as a town
Your graph as a town. Same style lock, footprint following the heatmap — the lone October cottage a sixth of the way along, then the June–August quarter. This is the personalisation shot the landing page should generate for a pasted username before any OAuth.
The commit atom: a cart delivering bricks
The commit atom. Each commit is one brick delivered by cart; every N bricks the crane lifts a floor; a merged PR tops the building out. The commit message scrolls in the Gazette: fix: retry OTP send — 3 bricks to the Post Office.

Why bricks are capped. Raw commits are the most gameable signal (gitfiti, activity generators) and the one that punishes the senior on a private monorepo who ships five big PRs a week. Bricks earn with daily diminishing returns and are never the headline number; Blueprints (merged PRs) are.

07 · Event → resource → visible town event

Four resources. CI is a state, not a resource.

Bricks (labour), Blueprints (validated work), Visitors (external attention), Morale (community). Justin's four sliders — Expansion / Technology / Happiness / Tourism — stop being resources and become the spend, each with a distinct visible outcome.

GitHub eventResourceVisible in townEarned?
Push / commitBricks — daily diminishing, cappedCart drops bricks at a site; scaffolding rises one tierEarned but gameable — cap it, never headline it
PR openedSmall Blueprints + reserved plotSurveyors stake a plot; sign shows the PR title; crane parksWIP towns should visibly have cranes
PR mergedBlueprints (primary building currency); bonus into others' reposTopping-out: crane lifts roof, windows light, ribbon-cut; size ∝ lines changed (capped)Most earned; Pull Shark tiers 2/16/128/1024
PR review / review commentMoraleInspector walks the site with a clipboard; approve = green stamp floats upUnder-celebrated elsewhere — a differentiator
Issue opened on your repononePothole / orange cone on a roadLegible; do not reward (spam vector)
Issue closedBricks small + Morale smallRoad crew fills the potholeEarned
Release publishedBlueprints × multiplier + Visitors burstFireworks over the tallest building + a named landmark with the tagRare — attach the share prompt here
Star receivedVisitorsTourist balloon lands; Starstruck tiers unlock viewing tower / observation deckExternal validation — weight above commits
ForkVisitors + a road segmentRoad exits the map edge toward a "sister settlement" outlineSubtle only
CI green / red on default branch (webhook)statePower plant hums, lights on / smoke, one district flickers; clears on next greenHonest, non-destructive
Discussion answered / sponsorshipMorale / Visitors + MoraleCrowd in the square; plaque / patron statueLow volume, earned
Streak (computed) / inactivityunlocks seasons / none7d lanterns, 30d festival, 90d biome; fog rolls in, cranes park; return = sunriseAdditive only; grace like Duolingo's freeze

Data reality. Public Events API: ≤300 events / 30 days, 30 s–6 h latency, no CI status. There is no read-only private OAuth scope (repo = read/write) — use read:user + GraphQL contributionsCollection counts, or a GitHub App with webhooks. Never ask for repo. Test the slider nouns with five users; one meaningful choice per stage (which district, which landmark for a release) may beat set-once sliders — agency over upgrade paths sustains engagement even with deterministic outcomes (Cutting, Gundry & Cairns 2019, n = 1,972).

08 · Render approach

Under 1 MB, 60 fps on a 2019 laptop, looks like the render.

PRIMARY

Vanilla Three.js · instanced low-poly kit · baked light · locked ortho-iso · zero post

  • One InstancedMesh per kit piece (60–80 pieces), vertex-coloured, textureless → ≈150–400 KB GLB after gltf-transform quantize + meshopt
  • AO baked to vertex colours + one 1024–2048 ground lightmap (WebP); hemisphere + one directional light; Lambert/Toon; optional soft outline
  • Day/night = lerp sun/sky/hemisphere + emissive windows; seasons = palette swap per instance colour
  • Budget ≈ 0.6–1.0 MB total (three.js ~155–200 KB gz, app 100–200 KB, kit, lightmap, sprites). Windland proves 1.8 MB with more content
  • Growth stages, allocation variety, events are all per-instance state — cheap
  • Cost: OG images need a client snapshot upload or headless Chrome; measure on an Intel iGPU early
FALLBACK · MID-TERM HEDGE

Pre-rendered isometric sprite atlas · PixiJS / canvas · the RCT pipeline

  • Same Blender / MagicaVoxel kit, ortho-rendered at RCT angles with a path tracer (free true GI, DoF) → WebP atlas ≈0.5–1.2 MB for 4 rotations
  • Trivial 60 fps; PixiJS batches thousands of sprites; isocity does a full builder with one atlas + two canvases
  • Server-side OG re-composited from the same atlas with node-canvas / sharp — no browser needed
  • Cost: static shadows, night via tint + emissive layer, multi-angle vehicle sheets, every new piece re-rendered

Ban list. Real-time shadow maps (GithubCity's failure mode). Kenney kits beyond blockout — CC0 kits are the asset-flip tell; budget 1–2 weeks of tech-artist for a bespoke tiered kit + bake before any living layer. Textures. Post-processing. Voxel engines (exported look ≠ reference). SVG/CSS only for the README badge.

Kit sheet
Kit sheet — the ~30 tiered pieces the tech-artist actually has to author: cottage → townhouse → mid-rise → tower, dirt → cobble → asphalt, well → clock tower → crane → spire, balloon, boat, train, cone, plant, park.
09 · UI in context

UI quiet, town loud.

The town page. No stat tiles, no green field. Four resource meters, four coarse council settings, a "since you left" strip, and a share button. Everything else is the town.

committown.io/janardhan
Town page
Commit Town
JanardhanpurTOWN · day 412Pop. 1,204
Town council
How new resources are spent
Expansion
Technology
Happiness
Tourism
Next choice unlocks at City: pick your first district.
Since you left · 3 days 🏗 Topping-out feat/otp-retry → Post Office 🎈 2 tourists landed (stars on measure) 🧱 +31 bricks · scaffold up on Block 47 🌫 fog lifted
Bricks
312
+31 today
Blueprints
18
+1 merged
Visitors
27
+2 stars
Morale
High
3 reviews this week
local 18:42 · golden hour · next: lights on 19:10

Coded, not generated. Coarse 4-step council settings instead of percentage sliders (a set-once slider is a widget, not a game); the strip at the bottom is the "what changed since you left" reveal that gives a passive product a reason to be opened.

The distribution surface: your GitHub profile

github.com / janardhan-dot
Janardhan
janardhan-dot
Design · BOQ tooling · Shopify apps
Follow
janardhan-dot / README.md
344contributions
18PRs merged
27stars
Townstage
Janardhan@janardhan · 2h
Janardhanpur just topped out its 18th building. Three quiet weeks of fog, then a June boom — the town remembers. 🏗
OG image
TOWN · day 412 · pop 1,204
Janardhanpur — Commit Towncommittown.io/janardhan

Ranked share units: (1) the README animated-SVG embed — every profile view is an impression and it self-updates; (2) a public URL with a live OG image; (3) event-triggered 4–8 s clips with the share prompt at the moment; (4) weekly "This week in Janardhanpur" as a retention email, not a viral unit; (5) a year-end Town Wrapped timelapse.

10 · Share unit

One composition, three crops.

The plinth diorama is chosen so a single frame works as a 1.91:1 OG image, a 3:1 README banner and a 9:16 clip. That is a design constraint on the camera, not an export setting.

OG crop
1.91:1 OG — X / LinkedIn / Slack unfurl.
Banner crop
3:1 banner — README embed, animated SVG.
Vertical crop
9:16 clip — the Reel format that actually drove Git City's growth.
12 · What I'd build first

A one-week test, and only then a kit.

  1. Week 1 — the fake door. Paste a username → public GraphQL counts → deterministic stage → one good render (this style, static or the loop) → OG card on a public URL. Post it once. Measure share-rate, referral visits per share, D7 return of sharers, and one question: "have you seen something like this?"
  2. Only if D7-of-sharers ≥ 10% and novelty ≥ 30%: commission the tiered kit + bake (1–2 weeks tech-artist), ship the README animated-SVG embed, then the ambient layer, then event beats via a GitHub App webhook.
  3. Never build first: the economy, the sliders, ads, a shop. Every predecessor built those and got $1,606.

Offer. If Justin wants it, the next artefact I would make is a Three.js proof-of-loop — the hero plate's geometry as an instanced kit with the ambient layer running at 60 fps under 1 MB — so the "living town" question is settled with a URL rather than a debate. That is a few days, not a project.