Polar Ice Sonification

Mark Ballora, Penn State Polar Center

The following compositions are a series of data sonifications representing changes in the Antarctic ice sheet over 400,000 years. The underlying data was collected by researchers at the Penn State Polar Center. This work is a data visualization and sonification of ice surface area, ice surface volume, floating ice area, floating ice volume, solar radiation, basal temperature, and sea level. Created in collaboration with Professor Mark Ballora and the Penn State Polar Center.

Floating ice area data visualization

Solar radiation data visualization

Sea level data visualization

Approach

Each variable in the polar dataset is mapped to a distinct synthesizer voice in SuperCollider. The total ice volume drives a resonant percussion instrument built from DynKlank — a bank of tuned resonators excited by random dust impulses. As ice volume changes, the resonator frequencies, trigger rates, spatial spread, and reverb characteristics shift, producing a texture that ranges from sparse, crystalline pings during ice minima to dense, shimmering clouds during glacial maxima.

Grounded ice area and volume control a low, droning oscillator that uses a cosine oscillator reading from a custom wavetable. The pitch and amplitude track the extent of land-based ice. Floating ice is represented by filtered noise bursts whose density and low-pass cutoff follow the floating ice volume, evoking the crackling texture of ice calving and fracturing.

Sea level drives a water-like synthesis built from resonant high-pass filtered impulses — a digital model of bubbling and dripping whose pitch, density, and spatial width respond to rising and falling ocean levels. Solar radiation controls a granular FM synthesis voice with comb filter delays, producing bright, shimmering tones whose frequency and spectral cutoff track insolation changes over millennia.

Basal temperature — the temperature at the base of the ice sheet — shapes a complex voice combining formant-filtered noise, resonant brown noise, and detuned sawtooth waves, producing a deep, vocal quality that shifts between warmth and tension as temperatures fluctuate.

The result is a six-voice composition that compresses 400,000 years of Antarctic climate history into an auditory experience where the listener can perceive glacial cycles, interglacial periods, and the interplay between ice, ocean, sun, and temperature as an evolving sonic landscape.

SuperCollider Code

s.options.sampleRate = 48000;
s.reboot;
(
s.boot;
~buffer1=Buffer.alloc(s, 512, 1, {arg buf; buf.sine1Msg(1.0/[3, 4, 7, 8, 1, 2])});
~path="/Users/mbk5020/Desktop/SONIFICATION/polar day/polar iterations/";

~polardata=CSVFileReader.read(~path ++ "polardata.csv", true, true).asFloat;
~data=~polardata.flop;
///basaltemp
~basalTemperatures=thisProcess.interpreter.executeFile(~path ++ "basal_temp_info");
~basaltemps=Array.newClear(~basalTemperatures.size);
~basalTemperatures.do({ arg item, i; ~basaltemps.put(i, item[1]) });
~masterAmp=1;
)
////////////////////////////////////////////////////Data
(
~size=4001;
~timedelta=0.1;

~groundedIceArea = ~data.at(4).normalize(30, 90);
~groundedIceVolume = ~data.at(1).normalize(0, 0.2);

~floatingIceArea  = ~data.at(5).normalize(0, 1);
~floatingIceVolume = ~data.at(2).normalize(0.1, 6);
~floatingIceVLFP = ~data.at(2).normalize(1200, 1600);

~sealevel = ~data.at(7).normalize(1, 4);
~sealevelAmp = ~data.at(7).normalize(0.01, 1);

~trigrates=~data.at(3).normalize(7, 25);
~attacks=~data.at(3).normalize(0.02, 0.2);
~spreads=~data.at(3).normalize(0.25, 1);
~damps=~data.at(0).normalize(0.25, 1);
~roomsizes=~data.at(0).normalize(0.5, 0.7);
~freqscales=~data.at(0).reciprocal.normalize(0.5, 1.25);
~noiselevs=~data.at(0).normalize(0, 0.0025);
~levels=~data.at(0).normalize(0.5, 2);

~earthFundamental=7.83; // lowest Schumann resonance
~fund=~earthFundamental*32;
~sunPitches=~data.at(6).normalize(~fund*(5/6), ~fund*(6/5));
~sunCutoffs=~data.at(6).normalize(2500, 10000);
~sunDetunes=~data.at(6).normalize(-1, 5);

~groundarea=~data.at(4).normalize(60, 90);
~noiserqs=~data.at(4).normalize(0.005, 0.02);
~temperaturesSawVol=~basaltemps.normalize(0.01, 0.035);
~temperaturepitches=~basaltemps.normalize(30, 45);
~temperaturesSawDetunes=~basaltemps.normalize(0.1, 1);
~temperaturesSawCutoffs=~basaltemps.normalize(180, 1000);
)