Skip to content
Luca-HacklPublic

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Circa

A dotted globe you can scrub through time. Zoom into a place, see what was happening there, and follow the threads connecting it to everywhere else.

The name is the point. Historical dates are ranges, not instants — circa 1200, sometime in the third century — and Circa is built to show that rather than round it away. A record the source dated to a year is displayed as a year, with within a year written underneath it.

Status: early, but real. The globe, the timeline, the detail panel and connection arcs all work, and they are showing 417,968 real events from the UCDP Georeferenced Event Dataset — organised violence worldwide since 1989, under CC BY 4.0. The synthetic fixture it was built against has been retired.

What it does

  • A globe drawn as a stipple of points masked to real coastlines
  • A time slider with a density curve showing where records actually exist
  • Playback that moves the window forward, with trails
  • A detail panel, opened by clicking a point or zooming close to one, carrying the record's date, location, source, licence, and coverage bounds

Roadmap

Ordered so each step produces something you can look at. Nothing here is pure plumbing — the momentum on a project like this comes from watching the globe get better.

Done

0 · Environment Toolchain, and verifying deck.gl's globe view exists and works. It is still exported as _GlobeView, so the version is pinned.
1 · The globe Golden-angle point field masked to real coastlines, jittered to kill moiré. Rotates, zooms.
2 · Schema and pipeline Canonical records, fuzzy time as Julian Day Numbers, source adapters, binary chunks, client loader.
3 · Time Window slider with a density curve, playback with trails, all of it kept out of React's render path.
4 · Detail Panel opened by click or by zooming close, carrying date, uncertainty, source, licence, and coverage. Plus a pre-rendered page per place.
5 · Connections Arcs between places. Every link in the window is drawn; selecting a place lifts its own out of the rest.
A real source 417,968 UCDP events. Detail paged, place ids made URL-safe, and the whole presentation retuned for a corpus four orders of magnitude larger than the fixture.
Five sources UCDP events, and four Correlates of War datasets — wars, militarized disputes, formal alliances, bilateral trade. 420,283 events and 39,600 links. Co-located records draw as one dot sized by count; neighbours merge by zoom and separate as the camera comes in.
Followable arcs The places arcs end at are drawn as rings and can be selected. Hollow, because an event dot claims something happened there and a capital only stands in for a state.
Shareable views The window, the camera and the selection live in the query string, so a find can be sent to someone. A truncated or hand-edited link opens the globe somewhere sensible rather than failing.
A 4 MB first load Down from 11.5 MB. The client place registry holds only the places links point at; the full one is a build input outside public/, and place names ride in the detail files that are fetched on demand anyway.
Narration Every record carries prose, and every narrative says who wrote it — the dataset (673, extracted from the PDFs Correlates of War publishes them in), or Circa, composed from fields. The three voices look different and say which they are.
/admin Unlisted dashboard over a stats file the pipeline writes last: totals, per-source contributions, storage by artifact, and real records shown verbatim.
An arc budget A 2012 window offers 12,377 connections and only a few hundred are readable, so a fixed budget is split evenly between sources and ranked within each — trade is dollars and war is battle deaths, and the two must never be sorted together. What is withheld is counted and stated.

Next

6 · Accounts Firebase Auth and saved views. The globe stays fully usable signed-out; accounts add persistence, never access.
7 · Deploy Firebase Hosting. Static export, no server.

Blocked, not scheduled

Connections after 2014 Every Correlates of War dyadic file stops between 2005 and 2018, so the most recent decade has no links at all. Closing it needs an editorial decision rather than an adapter: GDELT is current but machine-coded from news, Wikidata is CC0 but uneven.
ACLED Gated on its own terms, which unlike UCDP's are not plainly permissive.

Deliberately later

Place-states (what was true somewhere, and when) · deep-linkable routes · curated tours · search · export · mobile polish · a flat-map view for detail the globe cannot show · historical boundaries.

Principles

Three that shape the code more than anything else:

  • Nothing on screen without a source. Provenance is mandatory at ingest, so every record can be traced back to what published it.
  • Uncertainty renders, it does not round. Time is stored as four bounds, not a date. Precision is the degenerate case, not the assumption.
  • Absence is not evidence. An empty region usually means nothing was recorded, not that nothing happened. Sources declare what they cover so the app can tell the difference.

Design notes, decisions, and the reasoning behind them live in a separate Obsidian vault alongside this repo.

Layout

packages/core       canonical schema, time, binary codecs — no I/O, no React
packages/geo        build-time generation of the globe's dot field
packages/pipeline   source adapters and the build pipeline
apps/web            the Next.js app

Dependencies point one way: web → core, geo → core, pipeline → core. Nothing depends on web, and core depends on nothing.

Exactly one module imports deck.gl (apps/web/src/globe/Globe.tsx), and exactly one layer of code knows what any given data source looks like (its adapter). Both boundaries are deliberate.

Setup

Requires Node 20+ (developed on 24, which strips TypeScript natively — no build step for the pipeline, and no test framework).

npm install

Generate the data

Both outputs are build artifacts. They are gitignored: regenerate rather than commit.

Source datasets are downloaded rather than committed — they belong to their publishers, and UCDP's alone is 261 MB.

The globe's dot field needs Natural Earth land polygons (public domain) at packages/geo/.cache/land.geojson:

npm run generate:dots

Event chunks, detail pages, the density histogram, the place registry and the manifest. Needs UCDP GED at packages/pipeline/.cache/ucdp/GEDEvent_v26_1.csv, from https://ucdp.uu.se/downloads/ged/ged261-csv.zip (CC BY 4.0), plus the Correlates of War files each adapter names in its own error message — run it and it will tell you exactly what is missing and where to put it:

npm run generate:events

To see the render path without land data, generate an unmasked sphere:

npm run generate:dots -- --no-mask

Develop

npm run dev

Build

npm run build

Produces a fully static site in apps/web/out/. There is no server component — the build output is plain files, and sign-in and per-user data run through Firebase's client SDK with security rules as the authorization layer.

Checks

npm run typecheck
npm test

Tests use Node's built-in runner and Node 24's native TypeScript stripping, so there is no test framework and no build step. erasableSyntaxOnly is on in tsconfig.base.json for the same reason: syntax that emits code (enums, parameter properties) would typecheck and then fail at runtime.

Working on this

.claude/skills/ carries a vendored copy of Vercel's React and Next.js performance guidelines, committed so contributors get the same guidance the project was written against rather than each needing it installed locally. See its ATTRIBUTION.md.

Several rules in it are load-bearing here rather than incidental:

  • Import from @deck.gl/core and @deck.gl/layers directly. The deck.gl umbrella package is a barrel that pulls in every layer pack.
  • Keep transient values out of React state. The time window changes on every frame of a drag; it is written straight to the DOM and to deck.gl, and React renders only on discrete changes.
  • Load the globe with next/dynamic and ssr: false. WebGL has no server-side equivalent.

Licence

Not yet chosen for the code.

The data is separate and keeps its own terms regardless: UCDP GED is CC BY 4.0, which requires citation and permits commercial use — the citation is carried in the source manifest and shown in the app. Other datasets are less permissive; ACLED in particular restricts commercial use. Check each before adding an adapter for it.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages