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.
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.
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.
Band
Score
Step
Seed
Mutation
Wash
Glow
Very positive
≥ 0.8
3 fr
40%
0.5%
20
30
Positive
0.6–0.8
5 fr
30%
0.2%
35
15
Neutral
0.4–0.6
8 fr
25%
0.1%
40
—
Negative
0.2–0.4
12 fr
20%
0.05%
50
—
Very negative
< 0.2
20 fr
15%
0
60
—
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.
Story
Band
Score
Conf.
Asian stocks slide on US lending fearsBloomberg
Very negative
0.10
99.6%
Two NFL rookies praised as “psychopaths”New York Post
Very negative
0.11
98.4%
US stock futures rise on strong bank earningsCNBC
Very negative
0.12
96.8%
Streaming series casts its next villainVariety
Negative
0.27
69.9%
US strike on a boat in the Caribbean leaves survivorsCBS News
Positive
0.77
76.9%
North Korea parades a new ICBM for the ruling party’s 80thCNN
Very positive
0.87
95.2%
Pop star hits back at her ex-husband’s memoirYahoo News UK
Very positive
0.88
97.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.