Projects

← Projects

Visualisation · 2025 · Concept · Code · Visual mapping

The Spirit of the Digital Age: An Animated Tapestry of News

A live news feed rendered as a woven surface. Each story is classified in the browser by a sentiment model, and the result does not choose an image — it sets the conditions under which a cellular automaton grows. Upbeat coverage weaves fast, dense and luminous; bleak coverage thins out, slows down and stops renewing itself. The rule underneath never changes. What the piece ends up showing most clearly is not the mood of the news but what a sentiment model actually listens to.

Sentiment analysisCellular automataTransformers.jsp5.js
InputLive US top stories from NewsAPI, one per step
ModelQuantised DistilBERT (SST-2), run client-side with Transformers.js
MappingFive bands → tempo, seeding, mutation, trail, glow
Renderingp5.js · HighLife (B36/S23) on a wrapping grid

A binary model on a continuous axis

The reader steps through stories with a single control. Each step pulls the next story from NewsAPI and classifies its text — the opening paragraph where the API supplies one, otherwise headline and standfirst — with a DistilBERT model fine-tuned on SST-2, loaded through Transformers.js and run entirely in the page. SST-2 is a two-class task, so the model returns a label and a probability rather than a position on a scale. The score folds the two into one axis: the label picks the side, the probability picks the distance from the centre, softened by an exponent of 1.5 and capped at 0.1 and 0.9 so that no single story reads as absolute.

The mapping has a consequence worth stating plainly. A two-class model never answers neither: its winning probability is always at least 0.5, so a positive label lands between 0.64 and 0.9 and a negative one between 0.1 and 0.36. The neutral band exists in the mapping, but only a failed classification ever reaches it. The visualisation is always taking a side.
const confidence = Math.pow(sentiment.score, 1.5);
if (sentiment.label === 'POSITIVE') {
  score = 0.5 + (confidence * 0.4); // Max score will be 0.9, not 1.0
} else if (sentiment.label === 'NEGATIVE') {
  score = 0.5 - (confidence * 0.4); // Min score will be 0.1, not 0.0
} else {
  score = 0.5; // Neutral
}
sentiment.js — folding label and probability into one score

One rule, five regimes

The automaton runs HighLife — a dead cell is born with three or six live neighbours, a live one survives with two or three — on a grid that wraps at its edges, so a pattern leaving one side re-enters from the other instead of dying against a wall. That rule is fixed. Sentiment never rewrites it; it changes the conditions the rule runs under. The one place sentiment reaches inside a generation is mutation: every cell the rule leaves dead gets a small, band-dependent chance of igniting on its own.

Holding the rule constant is what makes the bands comparable. If each band ran different physics, the five states would be five unrelated animations and the difference between them would carry no meaning. With one rule, a change in tempo or density reads as the same system under different weather.
if (state == 0 && (neighbors == 3 || neighbors == 6)) {
  next[i][j] = 1;
} else if (state == 1 && (neighbors == 2 || neighbors == 3)) {
  next[i][j] = 1;
} else {
  next[i][j] = 0;
}
if (next[i][j] == 0 && random(1) < visParams.mutationRate) {
  next[i][j] = 1;
}
sketch.js — the fixed rule, then the only sentiment-dependent step

What each band changes

Each band sets five parameters. Step is how many frames pass between generations. Seed is how much of the grid is alive when a new story arrives and the grid is re-seeded. Wash is the opacity of the dark layer painted over the whole canvas every frame: low values let dead cells linger as afterimages, high values erase them almost at once. Glow draws a translucent halo behind each live cell.
BandScoreStepSeedMutationWashGlow
Very positive≥ 0.83 fr40%0.5%2030
Positive0.6–0.85 fr30%0.2%3515
Neutral0.4–0.68 fr25%0.1%40—
Negative0.2–0.412 fr20%0.05%50—
Very negative< 0.220 fr15%060—
Read together, the parameters pull the same way. Positive coverage is fast, crowded, self-renewing and leaves light behind it. Negative coverage is slow and sparse, and at the bottom band, with mutation at zero, nothing new can arise: the grid can only settle into still lifes and oscillators and stay there.
Four of the five bands, each a frame from the running system: very negative and negative above, positive and very positive below. Neutral is missing because the model never produced it.

The model hears the framing, not the event

Run against live coverage, the system's most legible output turned out to be a portrait of the classifier. SST-2 is built from film-review sentences, and the model scores news the way it would score a review: by the valence of the wording, not the weight of what happened. A rally in US stocks, written in the language of earnings, comes out as bleak as a credit scare. A military strike that left survivors, and a missile parade framed as a party anniversary, come out positive.
StoryBandScoreConf.
Asian stocks slide on US lending fearsBloombergVery negative0.1099.6%
Two NFL rookies praised as “psychopaths”New York PostVery negative0.1198.4%
US stock futures rise on strong bank earningsCNBCVery negative0.1296.8%
Streaming series casts its next villainVarietyNegative0.2769.9%
US strike on a boat in the Caribbean leaves survivorsCBS NewsPositive0.7776.9%
North Korea parades a new ICBM for the ruling party’s 80thCNNVery positive0.8795.2%
Pop star hits back at her ex-husband’s memoirYahoo News UKVery positive0.8897.0%
Two things follow. A confident score is not a correct one: the parade is read as very positive at 95%. And the gap between the tone a model hears and the event a story reports is not only noise to engineer away — it says something about how the story was written. That gap is what Dissonant Affect, my current research project, takes up as design material: setting the affective reading beside the framing so that readers can see where the two diverge.
Two readings from the running system. A casting announcement scores negative; a missile parade scores very positive, at 95% confidence.

← All projects