A curated, weighted, constantly-updating directory of venues that genuinely welcome free jazz, free improvisation, and avant-garde music — and the musicians keeping them alive.
Most "jazz venue" lists flatten everything together: the artist-run loft that programs free improvisation six nights a week sits next to the tourist bar that books a trad quartet on Sundays. Avant Atlas exists to tell those apart. Every venue gets an explainable commitment score, so you can see — and argue with — why a place is rated the way it is.
Project name. The repository directory is
freejazz-atlas; the product name is Avant Atlas. "Atlas" because the mission is geographic coverage — small scenes everywhere, not just the coasts.
- Weighting is the hard part, and the point. A venue's score (0–100)
measures commitment to this music, built from seven documented signals
(dedicated series, show frequency, artist roster, self-description, operating
model, community reputation, listening-room intent). See
docs/RUBRIC.md. - Every score is explainable. We store the signals and the evidence behind
the number — not just the number.
atlas show <venue>prints the full breakdown. - Calibrated to real anchors. iBeam (Brooklyn) → 98. Dissonant Works (St. Louis) → 94. Crosstown Arts (Memphis) → 47 (a broad arts center, not free-jazz-dedicated). Arthur's Tavern (West Village) → 10 (a commercial bar with occasional trad jazz).
- US-wide first, not NYC. The seed corpus deliberately spreads across 17 states + DC; New York City is only 4 of 32 venues.
- Built to grow geographically.
countryis ISO-3166 from day one, so Phase 2 (Europe) and Phase 3 (Asia) need new records, not new code. - Community-maintained. Human-editable YAML, one file per venue, full
provenance. Artists send small PRs; venues can self-list. See
CONTRIBUTING.md.
Live at atlas.mahakalamusic.com. (The intended home, , is routed and waiting on a DNS record in Miren's zone — see docs/DEPLOYMENT.md.)
# Core toolkit needs only Python 3.9+ and PyYAML:
pip install -e .
atlas stats # corpus size + geographic spread
atlas list --min 85 # every Cornerstone venue
atlas list --state IL # browse by state
atlas show ibeam-brooklyn # explainable score breakdown
atlas build # generate the 655-page static site + DIRECTORY.md
atlas linkcheck # is every cited website still reachable?
atlas verify # ranked queue: what a human should check next
atlas artistlinks --probe # find artists' sites, socials and dates pages
atlas events # scrape those dates -> gigs + artist↔venue edges
open site/index.htmlRun the web service (adds the submission form + JSON APIs on top of the site):
pip install -r requirements.txt
uvicorn web.server:app --port 8000Browse without any tooling: read DIRECTORY.md (generated), or
atlas build --single-page for a self-contained offline all-in-one.html.
atlas build emits a real multi-page site — not one giant page — because
geographic coverage is the mission and a single URL is invisible to anyone
searching for "free jazz in Lisbon":
/ map + browse (20 KB; the corpus is fetched, not inlined)
/venues/<id>/ one page per venue: score, seven signals, evidence, sources
/cities/<slug>/ every venue in a city (134 pages)
/regions/<state>/ every venue in a US state (38 pages)
/countries/<cc>/ every venue in a country (24 pages)
/artists/<id>/ where an artist plays (185 pages)
/tiers/<key>/ the rubric's tiers as lists
/rubric/ how scoring works, incl. what it can't do yet
/directory.json the whole corpus in one fetch
/sitemap.xml every page, with priorities
Every page carries a canonical URL, meta description, Open Graph tags and
schema.org JSON-LD (MusicVenue with geo + the commitment score as a
PropertyValue; Person for artists; BreadcrumbList and ItemList for
navigation). Tests assert that every venue has a page, every sitemap URL
resolves to a file, all JSON-LD parses, and the front page never grows back
into a 1 MB blob.
A venue directory rots — domains lapse, lofts become parking garages. Three mechanisms fight that, and none of them may silently change a score:
| Command | What it does |
|---|---|
atlas linkcheck --write |
Fetches every cited website concurrently and records ok / blocked / dead / unreachable in provenance. Deliberately distinguishes dead from blocked: a 403 from Cloudflare, a 400 from Facebook, or a TLS version mismatch tells us about bot defences, not about the venue. Only 404/410 and DNS failure raise needs_human_review. |
atlas recrawl --write |
Re-fetches each source page and compares a content hash — unchanged / changed / gone. A changed page flags the record for re-reading rather than re-scoring it. |
atlas verify |
Ranks the whole corpus by score impact × uncertainty (confidence, missing evidence, source count, staleness, link health) so scarce human attention lands where a wrong number does the most damage. |
scripts/refresh.sh chains all of it and commits the
result, so the corpus keeps its own history. It is meant to run unattended.
The first sweep found and fixed real rot: 18 of 259 cited websites were
broken; nine were repaired to verified current URLs (recorded in
provenance.url_corrections), and the rest are flagged in public on the venue
page itself rather than quietly left to look fine.
The crawler turns a public web source into a scored venue candidate:
pip install -e '.[crawl]' # adds requests + beautifulsoup4
atlas crawl https://some-venue.org/about --city Chicago --region IL --out cand.yaml
atlas recrawl --write # re-fetch sources, flag changed/closed venues- Fetch the page politely (identifies itself, times out).
- Extract visible text + metadata.
- Approximate the rubric signals with transparent, inspectable keyword lexicons, attaching the evidence snippet that drove each signal.
- Emit a low-confidence
unconfirmedcandidate for a human to verify.
The crawler never invents a committed venue. Its signal values are capped
below the "Cornerstone" range — the automated pass produces triage, and a human
gate turns triage into curation. The re-crawl pass (atlas recrawl) hashes each
source page to detect unchanged / changed / gone, keeping "active this
year" honest over time without re-reading every page by hand.
Sources it is designed to ingest: venue sites, event calendars, artist tour dates, festival lineups, label/collective sites, Bandcamp, and existing scene lists/wikis. (See Status for what's implemented vs. designed.)
- Artists & fans: add or correct a venue with a one-file YAML PR, or open an
issue. Add yourself or peers to
data/musicians/. Full guide inCONTRIBUTING.md. - Venues: self-list by copying the template and setting
operating_model/signalshonestly — a maintainer verifies before merge. - Every entry records who added it and from what sources. Low-confidence or
unverified entries are marked
needs_human_review: truerather than dropped.
atlas/ the toolkit
rubric.py the weights — single source of truth
model.py schema + validation
storage.py YAML load/save
crawl.py fetch a source -> scored candidate; content-hash re-crawl
linkcheck.py is the cited evidence still reachable?
links.py find an artist's site, socials and dates page
events.py read gigs off a page; match them to rooms in the corpus
site.py the multi-page static site generator
build.py orchestration: site + directory.json + DIRECTORY.md
cli.py the `atlas` command
web/server.py HTTP service: serves the site, submissions, JSON API
data/venues/ one YAML file per venue (canonical, human-editable)
data/musicians/ one YAML file per musician
data/events/ one YAML file per artist: their gigs, and what they matched
docs/RUBRIC.md the weighting rubric, with worked anchor examples
docs/DATA_MODEL.md the schema
scripts/seed.py how the seed corpus was generated (calibration record)
scripts/refresh.sh unattended freshness sweep (link health, re-crawl, rebuild)
deploy/ launchd agents + a systemd unit
docs/DEPLOYMENT.md how it is deployed, and how to move it
.miren/app.toml deployment config
site/ generated site (gitignored — rebuild with `atlas build`)
tests/ rubric calibration, validation, link health, site integrity
Real and working now:
- The rubric, calibrated to four anchors and enforced by tests.
- Data model + validation (
atlas validate), scoring (atlas score). - A corpus of 263 venues in 134 cities across 24 countries and 185 musicians, with per-signal evidence, source URLs and honest confidence.
- A 655-page static site with full crawler metadata, plus
DIRECTORY.md,directory.jsonand a JSON API. - A label-catalogue importer (
scripts/import_label_catalog.py) that grows the artist corpus from a label's own discography without inventing anything. - Link health, content-hash change detection, and a ranked verification queue.
- An events layer: artist link discovery, gig scraping from artists' own dates pages, and venue matching that records its reasoning — including for the misses.
- 76 tests covering rubric calibration, validation, link classification, site integrity (including HTML escaping of community-supplied text) and the credit-parsing rules of the label importer.
Known limitations — stated plainly:
- The corpus is LLM-seeded, not human-verified. Every record's provenance
says
seed:web-research-2026-07; 77 are flaggedneeds_human_reviewand 10 have no source URLs at all.atlas verifyexists precisely because this is the project's weakest property. Venue pages say so where it applies. - 40 of the 100 points are still estimates.
show_frequency(20) andartist_roster(20) remain human judgements. The events layer now exists and measures both, but it watches one artist so far — so the measurement is reported with a caveat and is not allowed to overwrite the estimate. Coverage is the work. - The corpus skews high — 61 cornerstones, only 9 venues below 45 — because the seed pass went looking for good venues. A rubric whose corpus has no negative space is not being tested very hard.
- Crawler signal extraction is keyword-heuristic and capped below "cornerstone" by design: triage, not judgement.
- Geocoding is by hand. Musician activity is human-maintained. There are no contact/booking fields yet.
Honesty note. Curation quality is the entire point. We would rather show a low confidence and a note about missing evidence than a confident wrong number. Corrections are welcome and expected.
- Events — started from the artist side. Scraping venue calendars is the
obvious way in and the worse one: a probe of 60 venue sites in this corpus
found machine-readable events on 4%. Artist pages are the better door, because
a working improviser keeps a tour list, that list names other people's
rooms, and every line is simultaneously evidence about a venue and an
artist↔venue edge.
atlas eventsreads schema.org JSON-LD, iCalendar, or plain "Oct 15: Elastic Arts, Chicago" text lines. Next: more artists with dates pages, then venue-side CMS adapters (WordPresstribe/events, Squarespace JSON, Bandcamp, Eventbrite, Dice) for the rooms nobody tours. - Artist graph — started, keep going. The first label roster (Mahakala
Music, 89 releases) took the corpus from 44 to 185 musicians with sourced
instruments, groups and discographies. The importer is label-agnostic, so
Clean Feed, Intakt, Astral Spirits, Catalytic Sound, Relative Pitch, Trost
and Not Two are the same command again. The hard part is the other half:
141 of those artists have no venue links yet, and artist↔venue edges are what
make the
artist_rostersignal computable — they fall out of event data for free once events land. - Contact + booking fields.
contact,booker,pay_model, PA/piano, accessibility, capacity (only 99/263 have it). This is what makes touring musicians want to maintain the data. - Verification passes down the
atlas verifyqueue, starting with the 10 zero-source records.
Code: MIT. Data (/data): CC0 1.0 — see LICENSE and
data/README.md.