shipped bandgraph in late march 2026. it’s an interactive visualizer for shared-member networks between bands — you type a seed artist, it pulls from musicbrainz, and d3 draws a force-directed diagram of who’s connected to whom through shared personnel. all the graph algorithms run in the browser.
since the first cut, the page is live-build first: you pick caps (bands, traversal depth), optionally include solo projects as first-class nodes, and if musicbrainz returns several matches you get an inline chooser instead of guessing the wrong entity. after something is on screen, click any band node to run a second, tighter bounded expansion from that node and merge it into the same graph — handy when the auto-walk stops at the caps but you already see where you want to grow next. builds can end in a partial state (timeout, queue cap, or max node/edge/request limits); the status line spells out why so you’re not staring at a half graph wondering if it crashed. there is also png screenshot export, a collapsible debug console, pan/zoom on the svg, and hover tooltips on nodes (member lists) and edges.
what it does
the core idea is pretty simple: two bands are connected if they share at least one member at some point in their histories. once a graph is built, d3 lays it out as a force simulation where nodes are bands (and optionally solo projects) and edges are those personnel overlaps. in the viewport you can toggle collaboration edges — where bands have worked together without sharing a full member — as a softer connection type; the path demos can include or ignore them independently.
on top of the viz there are three interactive algorithm demos:
shortest path — given two band names, find the minimum-hop route between them through shared members. classic BFS. you can see exactly which members form each bridge.
neighborhood — pick a seed band and a depth, get back all bands reachable within that many hops. supports BFS and DFS traversal strategies, and the result highlights in the graph.
bipartite walk — this one’s weirder and more fun. the graph is actually bipartite: bands on one side, individual musicians on the other. the walk alternates band → member → band → member, tracing how a specific person connects you from one band cluster to another. again BFS or DFS.
all three were originally written as python scripts (algorithms.py, run.py) for building and querying graphs offline. i ported them to a single client-side algorithms.js module so the lab doesn’t need a backend to run the demos on whatever graph is loaded.
how it came together
step one — frozen graphs, prove the stack. before worrying about the browser talking to musicbrainz, i wanted the viz and algorithms correct on known data. a python ingest script hits the API, runs the same bounded BFS expansion from a seed band, and writes { nodes, edges, metadata } json. that gave repeatable fixtures to tune d3 and debug BFS/DFS without network noise. three seeds i kept coming back to:
- title fight — 22 bands, 55 edges, depth 3. dense; the scranton/new haven scene is genuinely interconnected.
- bladee — 40 bands, 43 edges. drain gang adjacent universe, surprisingly spread out.
- yung lean — 24 bands, 24 edges. smaller, sparser, mostly sad boys era.
step two — same shape, live in the tab. the next move wasn’t a separate product line; it was lifting the ingest logic into builder.js and musicbrainz.js so the expansion runs client-side. the graph object matches what the python pipeline emitted, so the force layout and algorithm panels didn’t need a second code path — same structure, different producer.
BandGraphConfig still keeps things polite to musicbrainz: about one request per 1.1s, separate hard caps for the initial crawl vs the smaller click-to-expand crawl, a queue size ceiling so traversal stays predictable, and an abortable per-request timeout (two minutes) so a stuck fetch does not block the tab forever. a rolling hourly call counter lives in localStorage for a soft budget; crossing the configured threshold only emits a console warning so you notice before you hammer the api. browsers will not send a custom User-Agent, so the string in config is really there for a hypothetical same-origin proxy; what the client does set is a stable random id in localStorage keyed for that purpose. starting a new build aborts any in-flight traversal via AbortController.
still tempting later: a tiny same-origin proxy (e.g. cloudflare worker at /api/musicbrainz/*) if cors, headers, or quota get painful — not because the static-json era and the live builder were competing designs, but because the polite way to call a public api from the browser is the part that keeps evolving.
the other changes in this push
beyond bandgraph itself, this commit also:
- dropped the light/dark toggle — paper-white is now the permanent palette. i kept going back and forth on whether to support dark mode and ultimately decided the visual identity of the site is tied to the paper texture and warm white. one palette, commit to it.
- semantic link colors — moved link styles into
colours.cssso they’re centrally defined. sage green default, darker sage on hover. feels more considered than just inheriting browser blue. - homepage now points to
/bandgraph/as the projects entry point, which is a slight reframe — treating the band graph as the hub for project-type content rather than having a separate projects section.
why band graphs
started this because i kept noticing how tangled indie and underground music scenes actually are when you trace personnel. title fight is a good example — you start pulling that thread and end up somewhere completely unexpected within three hops. the graph makes that visible in a way that’s hard to convey otherwise.
this will be my term project for my graph algorithms course; one that i primarily took for a purpose like this. i had envisioned a “music web” a long time before i realized that graph theory could be used to model it. eventually, i’ll flesh out one specifically for atlanta/georgia music and arts for the gutterball connectome.