The Amsterdam Data Project #02

The making of “PM2.5”

George Galanakis
9 min readDec 28, 2020

PM2.5 is part of the Amsterdam Data Project — a series of massive public data sculptures on all the giant LED media screens around Amsterdam.

The Netherlands is a leader in public open data and I’ve been lucky enough to be supported by and collaborate with the folks at Ngage Media in the Netherlands, to take part of a year long exploration into the wonderful world of open data in the Netherlands.

Air pollution is the single largest environmental cause of premature death in urban Europe.

More than 80% of people in European cities live in air quality below World Health Organization standards. And globally more than 91% of the world’s population is living in places where the WHO air quality guidelines levels were not met.

This is mad. The following statistics are even more so…

In 2014 PM2.5 was linked to 500,000 premature deaths in Europe — around 75% of all early deaths on the continent. Globally this figure was around 7 million (to put this in perspective around 3 million people were killed each year during WW1). Yet very few people have even heard of PM2.5.

When we think of pollution, we tend to think of maybe Carbon or Nitrogen emissions. However, it’s a pollutant that most people are not even aware of — fine particulate matter (or PM2.5) — that what we should be worrying about.

PM2.5 are tiny particles in the air, that are smaller than 2.5µm (micrometers). They’re what cause reduced visibility and the air to appear hazy when their levels are elevated. And they can have the effect of making tiny incisions in our lungs causing asthma, reparatory inflammation, jeopardizing lung functions and causing accelerated aging of the lungs, causing bronchitis, heart attacks, kidney damage, and cancer. PM2.5 has the ability to absorb toxic substances such as heavy metals and micro-organisms, further enhancing the toxicity of PM2.5.

In the Netherlands urban areas have a PM2.5 concentration of 14.9µg/m³. The WHO recommends 10µg/m³. In 2016 environmental organization Milieudefensie suggested that the air pollution in Amsterdam was the equivalent of smoking 6 cigarettes a day and actually won a court case against the Dutch government to do more to reduce pollution levels.

Here’s a scary graph of what the World Bank’s is saying about the global rise of PM2.5 pollution, which is pretty mad (remember the WHO says your annual exposure should be 10µg/m³):

Here’s how the world’s PM25 values are “off the chart” — https://data.worldbank.org/indicator/en.atm.pm25.mc.m3?end=2016&start=2010&view=chart

While things indeed do look pretty dire, in Europe, and the Netherlands, it is improving, having halved their pollution output since the 1990s, showing that current reduction programs are going a long way to making our cities more livable.

The data sculpture was coded completely in Javascript, using my own library that I’ve been developing over the past few years from my #Code365 project. The data was obtained from the wonderful data.amsterdam.nl portal.

I think it is quite unique to be using Javascript for large scale installations, so the following is an account of how I would go about building a large-scale data project like this, and will hopefully give you some insight or inspiration into building your own.

The Making of Pm2.5:

I am less concerned about creating a true visualization of the data, and more of creating a feeling that represents the data in an abstract and beautiful way. Just like a sound visualization is better when it is not just a literal reflection of the data — like an EQ or visualization of the sound waves, data art takes the data as an input to create something beautiful, that gets the overall feeling of the data across. My methodology when starting out a new data sculpture is to get a feel of the data, which involves plotting it out and seeing its movement.

The dataset I has acquired for this artwork was pollution data collected from 89 air quality measuring stations in Amsterdam between 2013 and 2016.

I immediately knew I wanted to do a visualization based on PM2.5, having researched this topic for a number of years. I first heard about PM2.5 at the awesome Dim Sum Labs in Hong Kong when some NGOs came to talk about it, at the and having worked on a PM2.5 physical art project in Shanghai a few years ago (where the PM2.5 AQ index can get into the toxic 200s).

However, out of curiosity, I also wanted to take a look at the other pollution data. So I quickly knocked up a visualization on a Google Maps using the amazing CanvasLayer.js library, which makes it super easy to do Javascript canvas stuff on top of a map

This is the one year mean of 14 of the heaviest polluters which didn’t tell me a lot, except that the nitrogens and particulate matter (PM2.5 and PM10) values were dominating…. but it did help me to start organizing my code and thoughts around the data …

All pollutant measurements

Here are the 18 measuring stations’ mean PM2.5 data compared to World Health Organization maximum yearly exposure recommendations — the grey circles indicating when a measurement is deemed to be in the safe range (which is almost never):

PM2.5 values across the Netherlands from 2012–2016. If a value is grey then it’s below the WHO threshold

While PM10 (aka course particulate matter) originates mainly from mechanical processes such as construction sites, road dust resuspension, PM2.5 (fine particulate matter) originates primarily from combustion sources. This is both PM2.5 and PM10, again, grey indicates when a measuring station falls within the WHO standards:

PM25 and PM10 values combined compared to WHO maximums. If a value is below the maximum, it is grey, which is almost never

I then must admit, that I went down a rather long and deep rabbit hole. As I had been delving quite deeply into TouchDesigner, I decided I wanted to do the whole sculpture the node-based software. I had the idea of representing the pollution data in a type of metaball lava lamp effect. And after spending ages and ages on it, the output never really made me happy.

I’ll just rush through where I was going with it — I will be writing another article on data wrangling in TD): I normalized data over the Netherlands and converted it into a kind of pixelated heat map, that I used to spawn my metaball particle system. The trick to getting the metaballs, thanks to help from TouchDesigner guru and all-round super nice guy, Matthew Ragan, was to use a Convert and a Subdivide TOP. Here’s a portion of my messy TouchDesigner workspace:

And the output, after weeks of tweaking and tweaking, and wondering how to do stuff, and hassling a lot of people on various forums was a visualization that looked something like this:

The rabbit hole… My TouchDesigner output that I eventually abandoned.

I, however, eventually wasn’t satisfied with the output, and the story and feeling it was conveying, and so (after a loooong detour) went back to drawing board. And back to Javascript.

One of the issues I was having was with how to convey the geographical element. In the first edition of the Amsterdam Data Project — Counting to a Million, with an aerial approach, it was relatively easy. But there’s only so many top down interpretations you can have. The screens, especially those at Amsterdam Central Station are super wide, 2112x608, meaning it hard to fit the map in without leaving a lot of space on the sides, or missing a portion of the country.

And when doing a East/West view looking across the Netherlands, the longitudes distort the data closest to you making it seem more important. In the TouchDesigner piece, I squashed the longitudes closer together to try and avoid this effect, but still show longitudinal depth. And this still gave the feel that the data “in front” was more important. I tried spinning the camera around, but this just confused things even more.

So the solution I finally came up with, was to ignore all longitudes, or rather treat them equally and only look at latitudes. As the Netherlands is kind of long and thin, I think this worked out quite well. And again, for my sculptures, I’m not trying to build an accurate data visualization, but rather to give a feeling that the data conveys.

Once I had figured out the location problem, things went relatively quickly from there…

I used a little trick I use often to group the latitudes together, but still give them a sense of real space/distance. It’s a function I call sticky, for the very reason that it makes numbers stick, or remaps them, to a range:

function sticky(num, clamper) {
clamper = clamper | 1;
return Math.round(num / clamper) * clamper;
}
console.log( sticky(104, 10) ) // 100
console.log( sticky(88, 10) ) // 90
console.log( sticky(83, 10) ) // 80
console.log( sticky(72, 10) ) // 70

And mapped the yearly values for each grouped latitudes to an animating graph:

Height represents PM2.5 measurement, width represents latitude

One I had the graph going I had a good idea where I wanted to go… to convey a feeling of that the pollutions stays in our environment, of it rising and swirling around.

Attached to each bar is a rotating axis, and attached to that an ellipse, the size and speed of which is determined by its PM 2.5 value. I use two different canvases, on two draw the foreground and one to draw the background, which fades slowly over time, using a modulo like so:

if (frameCount % 60 == 0) ctx.background(0, 0.01);

The ctx.background() function is just shorthand for drawing a rectangle over the canvas, taking in colour arguments, similar to Processing’s background() function.

The final result being spinning the orbs that leave trails in their wake…

All that was left was tweaking, setting the type and finally choosing a colour combination — which involves testing live on the big big screens and seeing what works. Special thanks to Daan Krijnen from Ngage for all the tests, patience and support, without which this project would never have happened.

Here’s some of the tests Daan did for me remotely:

I love how the couple takes a selfie here… but ultimately I felt the white looked kind of inverted
This was my first choice on my computer…. but maybe too screensaver-ish on the big scree
The whites are very white, but I like how the pollution fills up the white over time… and ultimately went for a darker version of this

I’ll add some of the final live outputs soon.

And that’s about it. Hope you found something interesting here. Follow me on Instagram for updates and to see more of my #code365 project.

Amsterdam PM2.5 is currently viewable on the giant media screens at Amsterdam Centraal Station, Leidsplein (near the Apple Store) and Amsterdam-Zuid WTC.

Some other quick technical bits that may or may not be of interest:

Once the visualisation is complete, I do a simple screen capture for the various screen sizes that is used for the final output. My screen capture tool of choice is the amazing (and free) Monosnap, which is way less heavy on resources that doing a capture from Quicktime, it also lets you save out your captures as (rather large) gifs.

I have a url hash system that I use (similar as I do with my VJ app) that will re-size the visualisation canvas for various screen sizes.

So for reducing the size of animated gifs for this article I used gifsicle, particularly this command, to reduce the file size by removing every second frame:

gifsicle -U input.gif `seq -f "#%g" 0 2 99` -O2 -o output.gif

I am extremely grateful for the folks at Ngage Media, the Netherlands’ leading network in Out of Home digital screens who made this project possible and specifically Ngage’s Daan Krijnen for his patience and for believing in this unique collaboration.

If you want to get started in creative coding, I have a bunch of tutorials here.

And please follow me here if you so desire:

https://www.instagram.com/radarboy3000/

https://twitter.com/radarboy_japan

https://www.facebook.com/radarboy3000

--

--

George Galanakis

Media artist, tinkerer, dreamer. Generative motion, sound and visualizations. Data sculpture and Code Art. https://www.instagram.com/radarboy3000