00 CONCEPT
ΑΛΛΟΤΡΟΠΙΑΣ Ec(h)o-Processing | Seismic Audio Modulation SystemTechnical dev_ ValeriPS³ 11 Rosemary Street, Belfast BT1 1QA Saturday 21 March – Thursday 2 April Late Night Art (6–9pm) on 2 April Artists: Rosa Catalano Donatella Guarino Marina Iodice Informed by Marina Iodice's PhD research, Ulster University ───────────────────────────────────────────────────────────────── The exhibition links geological and social instability across two territories — Vesuvian and Northern Irish — through projected graphs, artefacts, maps, sound, and video. The artists evoke instability as both a physical and social condition, where ambiguous traces are shaped by tension and transformation, whilst depictions of horizons and boundaries imply unseen forces at work.
01 AUDIO SIGNAL CHAIN
[ AUDIO SOURCE ]
│ 3 × ambient recordings .mp3 (loop)
│ selectable channels: TRACK 1 / 2 / 3
│ nominal duration: ~330 s per file
▼
[ DELAY NODE ]
delayTime: 0.08 s – 0.35 s
(controlled by seismic cluster density)
│
▼
[ FEEDBACK GAIN NODE ]
gain: 0.10 – 0.65
(cluster density → gain)
│
└── feedback loop returning to delay
▼
[ MAIN GAIN ]
nominal: 0.70
on seismic event: glitch envelope
ML < 2.6 → +20% peak, duration 0.3 s
ML ≥ 2.6 → cut to 0.0 for 0.1 s
return in 2.5 s
▼
[ BIQUAD LPF FILTER ]
type: low-pass
resting state: 18,000 Hz
on event: closes to 4,000 Hz
Q: 1.2
▼
[ ANALYZER ]
fftSize: 2048
feeds the waveform canvas (section A)
▼
[ COMPRESSOR ]
threshold: –1 dBFS
ratio: 20:1
attack: 1 ms
release: 50 ms
▼
[ AUDIO DESTINATION / SPEAKERS ]
02 SEISMIC DATA → AUDIO PARAMETER MAPPINGS
Three seismic parameters are extracted per event and mapped
continuously to the audio chain. Each operates independently.
─────────────────────────────────────────────────────────────────
PARAMETER 1 — MAGNITUDE (event-triggered)
─────────────────────────────────────────────────────────────────
source: ML (local magnitude) from INGV seismic log
norm: ML / 4.4 → 0.0 – 1.0
ML < 2.0 → MINOR glitch
gain spike +20%, duration 150–250ms
4-cycle opacity flicker on display
2.0 ≤ ML < 2.6 → MINOR / STRONG threshold
gain spike, lpf partially closes
ML ≥ 2.6 → STRONG event
gain cuts to 0.0 for 0.1s
returns over 2.5s exponential curve
lpf closes fully to 4,000 Hz
terminal log entry generated
ML ≥ 3.0 → MAINSHOCK
extreme display corruption
8–12 title glitch pulses
monitor shake animation triggered
detection window: ±60s from current sequence position
event cooldown: tracked per event ID (no re-trigger)
─────────────────────────────────────────────────────────────────
PARAMETER 2 — DEPTH (continuous)
─────────────────────────────────────────────────────────────────
source: depth in km from hypocenter data
norm: (depth – 1.5) / (5.0 – 1.5) → 0.0 – 1.0
shallow (1.5km) = 0.0 → playbackRate 0.93 (low)
deep (5.0km) = 1.0 → playbackRate 1.04 (high)
effect: audio.playbackRate (Web Audio API)
preservesPitch = false
→ pitch and speed both shift together
transition: exponential smoothing α = 0.02 / animation frame
~5–8 seconds to reach target value
creates very slow drift, barely perceptible
─────────────────────────────────────────────────────────────────
PARAMETER 3 — CLUSTER DENSITY (continuous)
─────────────────────────────────────────────────────────────────
source: events per 10-minute window around current position
norm: cluster_count / 15 → 0.0 – 1.0
targets:
delayTime → 0.08s + norm × (0.35 – 0.08)
feedbackGain → 0.10 + norm × (0.65 – 0.10)
transition: AudioParam.setTargetAtTime(), τ = 0.7s
→ smooth ramp, no zipper noise
effect: at low density (isolated events): short, sparse echo
at high density (swarm): long, saturated echo
→ voice multiplication, unstable overlap
─────────────────────────────────────────────────────────────────
MECHANICAL LAYER — WOW & FLUTTER (always active during playback)
─────────────────────────────────────────────────────────────────
independent of seismic data — simulates tape degradation
WOW (slow): brownian random walk
frequency: 0.05 – 0.12 Hz
amplitude: ±0.003 – ±0.007 playbackRate
mean-revert: restore = 0.003, friction = 0.85
re-randomize: every 18–24s
FLUTTER (fast): brownian random walk
frequency: 0.15 – 0.35 Hz
amplitude: ±0.001 – ±0.004 playbackRate
mean-revert: restore = 0.005, friction = 0.88
final playbackRate = seismic_depth_drift + wow + flutter
= (0.93–1.04) + (±0.007) + (±0.004)
= total range approx 0.919 – 1.051
perceptibility: ±0.4–1.1% — below conscious threshold,
above zero → the signal is
always slightly wrong
03 DISPLAY — SECTION A: SEISMOGRAPH
Real-time waveform trace of the seismic influence magnitude.
Drawn on a canvas element, updated every animation frame (~60fps).
┌───────────────────────────────────────────────┐
│ amplitude │
│ +1.0 ┤ │
│ │ ╭─╮ ╭──╮ │
│ │ ╱ ╲ ╱ ╲ ╭╮ │
│ 0.0 ┤──╱─────╲─────╱──────╲──╱──╲───────────│
│ │ ╲ ╱ ╲╱ ╲. │
│ │ ╲─╱ ╲───. │
│ –1.0 ┤ │
│ └───────────────────────────────────────│
│ t=0 t=now │
└───────────────────────────────────────────────┘
x axis: time — scrolling left, each frame adds one sample
maxDataPoints = canvas.width (auto-resizes to element)
y axis: seismic amplitude — calculated per frame:
if seismicMod active and magnitude > 0:
amp = sin(ct × freq × π) × base × 0.3
+ sin(ct × freq/2 × π) × base × 0.7
+ random() × base × 0.2
where base = ML / 4.4, freq = 2 + (ML × 0.5)
else:
amp = (random() – 0.5) × 0.01 (noise floor)
scale: auto-adjusts to peak value, smoothed α = 0.1
→ waveform always fills the canvas vertically
color: rgba(255, 255, 255, 0.6) solid stroke
no fill, no grid, no labels — pure trace
04 DISPLAY — SECTION B: DOT MATRIX
A 150-node grid representing population displacement from
Belfast during the Troubles (1969–1973).
Each lit dot (●) stands for approximately 400 displaced persons.
As the installation runs, dots extinguish one by one, following
the chronology of historical displacement waves. Each
disappearance is irreversible for the duration of the
performance — no dot returns.
total represented: 150 × 400 = ~60,000 persons
source period: 1969–1973 displacement events
┌───────────────────────────────────────────────────┐
│ ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● │
│ ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● │
│ ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● ● │
│ ● ● ● ● ● ● ● ● ● ● ● ○ ○ ○ ○ ○ │ ← extinguishing
│ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ │
│ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ │
│ │
│ ● lit = person still present │
│ ○ unlit = person displaced / removed │
└───────────────────────────────────────────────────┘
wave chronology (extinguishing order):
1969 — August riots, first mass displacement
1970 — continued movement, intimidation campaigns
1971 — largest single-year displacement
1972 — Bloody Friday, accelerated clearances
1973 — residual movement, community fragmentation
rendering:
canvas 2D, dots drawn as filled arcs
active: rgba(255,255,255, 0.7) radius 3px
extinguished: not drawn (void is literal absence —
the dot does not fade or grey out,
it simply ceases to exist in the grid)
layout: auto-grid, margin 12px, spacing calculated from
canvas dimensions / sqrt(150) per axis
05 DISPLAY — SECTION C: VOID BARS
Four horizontal bars, one per year (1969–1972).
Each bar represents the volume of families displaced
in that calendar year — specifically the inhabitants
evacuated from Rione Terra (Pozzuoli), the ancient
hilltop neighbourhood progressively cleared as ground
uplift from bradyseism rendered it uninhabitable.
┌───────────────────────────────────────────────────┐
│ │
│ 1969 [░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│ ]│
│ └── void │
│ 1970 [░░░░░░░░░░░░░░░░░░░░░░░│ ]│
│ │
│ 1971 [░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│ ]│
│ │
│ 1972 [░░░░░░░░░░░░░░░│ ]│
│ │
│ ░ fill = families still present / accounted for │
│ void = families removed (empty space, no fill) │
│ border-right of void = marker line │
└───────────────────────────────────────────────────┘
displacement chronology — Rione Terra, Pozzuoli:
1969 — first bradyseismic unrest; structural surveys begin,
initial voluntary relocations of at-risk families
1970 — municipal evacuation order issued; majority of
remaining residents cleared from the rione
1971 — forced clearance of holdouts; area sealed;
largest single-year displacement count
1972 — final residual families removed; Rione Terra
abandoned and closed to habitation
mechanics:
each bar has two layers:
.void-bar-fill — white fill, right-to-left, width = 100%
at start, depletes as log processes
.void-bar-void — transparent, left-to-right, width = 0%
grows as displacement events are logged
right edge = 1px white line (the front)
transition: cubic-bezier(0.25, 0.46, 0.45, 0.94), 2s
on depletion event: pulse animation, 0.8s ease-out
data source:
Menoni, S., Galderisi, A., Carrion, D., & Gerosa, C. (2024)
"Cross-Sectoral and Multilevel Dimensions of Risk and
Resilience Management in Urban Areas Enabled by
Geospatial Data Processing"
Sustainability, 16(19), 8712
https://www.mdpi.com/2071-1050/16/19/8712
— displacement of inhabitants from Rione Terra, Pozzuoli
06 DISPLAY — SECTION D: GROUND UPLIFT GRAPH
Ground uplift curve for the Campi Flegrei area (Pozzuoli),
plotted as a continuous ascending line on a canvas element.
The data traces the vertical displacement of the ground
surface caused by bradyseism — the slow, episodic inflation
of the volcanic system beneath the bay.
┌──────────────────────────────────────────────────┐
│uplift │
│(cm) │
│+300 ┤ ●. │
│ │ ╱ │
│+200 ┤ ╱ │
│ │ ╱╲ ╱ │
│+100 ┤ ╱ ╲╱ │
│ │ ╱╲ ╱ │
│ 0 ┤──────────────╱──╲──────╱────────────────── │
│ └──┬──────┬──────┬──────┬──────┬──────┬─── │
│ 70 74 82 84 00 24 │
└──────────────────────────────────────────────────┘
key episodes:
1969–1972 — first bradyseismic crisis, Rione Terra evacuated
1982–1984 — second crisis, major uplift acceleration,
further evacuations from Pozzuoli centre
2000–2024 — renewed unrest, cumulative uplift reaching
record levels by the time of this dataset
the graph moves only upward.
the ground does not return.
source:
Menoni, S., Galderisi, A., Carrion, D., & Gerosa, C. (2024)
"Cross-Sectoral and Multilevel Dimensions of Risk and
Resilience Management in Urban Areas Enabled by
Geospatial Data Processing"
Sustainability, 16(19), 8712
https://www.mdpi.com/2071-1050/16/19/8712
— ground movement / vertical displacement data
07 TERMINAL LOG — SYNTHESIS ENGINE OUTPUT
The terminal displays real-time synthesis state per event.
Format on seismic event trigger:
══════════════════════════════════════════
EVT 20/05/2024 12:05:00 UTC [STRONG_AF]
── SEISMIC INPUT ─────────────────────────
ML=2.80 z=4.20km cluster=6ev/10min
P_arr=+0.700s S_arr=+1.200s ΔP-S=0.500s
norm_mag=63.4% norm_dep=77.1% norm_clu=40.0%
peak_inf=7.230 seqPos=2100.0s/41400s
── SYNTH ENGINE STATE ────────────────────
[PITCH_DRIFT] rate=0.9712 →0.9680 Δ=–0.47st
[DELAY_ECHO ] time=189ms feedback=0.320
[GAIN_FILTER] gain=0.735 lpf=9080Hz (50.4%) Q=1.2
[GLITCH_ENV ] type=SPIKE_ENV duration=300ms→1000ms
[COMPRESSOR ] thr=–1dBFS ratio=20:1 atk=1ms rel=50ms
══════════════════════════════════════════
event types:
[MINOR_AF] ML < 2.6 minor tremor
[STRONG_AF] ML 2.6–3.5 significant event
[MAIN_AF] ML ≥ 3.5 mainshock
P_arr / S_arr: estimated wave arrivals at surface
calculated from depth and standard
crustal velocity model (Vp=6.0km/s, Vs=3.5km/s)
ΔP-S: P–S interval → proxy for source distance
norm_* values: all 0–100%, used directly as modulation depth
peak_inf: composite influence score driving fragility engine
08 FRAGILITY ENGINE
A continuous background process that introduces visual
degradation proportional to the current seismic state.
Runs independently of event triggers — it is ambient instability.
─────────────────────────────────────────────────────────────────
FRAGILITY VALUE f ∈ [0.05, 1.0]
─────────────────────────────────────────────────────────────────
without seismic mod:
f = 0.05 (baseline noise, always present)
with seismic mod active:
f = (norm_magnitude × 0.72)
+ (norm_cluster × 0.28)
+ 0.05
→ magnitude contributes 72% of fragility
→ cluster density contributes 28%
→ baseline 5% ensures the system is never fully stable
─────────────────────────────────────────────────────────────────
CYCLE INTERVAL
─────────────────────────────────────────────────────────────────
interval = lerp(5000ms, 800ms, f) ±20% jitter
f = 0.05 → next effect in ~5000ms (quiet)
f = 0.50 → next effect in ~2900ms (active)
f = 1.00 → next effect in ~800ms (crisis)
─────────────────────────────────────────────────────────────────
EFFECTS (one per cycle, weighted random selection)
─────────────────────────────────────────────────────────────────
TERMINAL CORRUPTION
probability: always available
target: 1 random line in terminal log
intensity: 3% – 21% of characters replaced
character set: █▓▒░│┤╬═▀▄■╠╦╩
duration: 60ms – 180ms, then restored
opacity: 0.55 – 0.90 during corruption
CRT POWER DIP
probability: increases with f
target: entire monitor opacity
floor: 0.35 – 0.80 (lower = more severe)
cycles: 1 – 2 flicker pulses
effect: simulates power brownout
TITLE GLITCH
probability: low at f < 0.3, high at f > 0.7
target: ΑΛΛΟΤΡΟΠΙΑΣ title (canvas + credits)
intensity: 3% – 21% character substitution
duration: 80ms per pulse
both instances glitch simultaneously
─────────────────────────────────────────────────────────────────
DISPLACEMENT LOG GLITCH (on event, separate from fragility)
─────────────────────────────────────────────────────────────────
triggered on each displacement log entry:
header lines: 2-cycle opacity flicker 0.3↔0.9, 55ms
pattern lines: flicker + character corruption ∝ intensity
data lines: shimmer if intensity ≥ 0.7
extreme 4-cycle white flash if ≥ 0.95
09 VIDEO SYSTEM
Three video channels (VIDEO 1 / 2 / 3) cycle automatically
when the VIDEO switch is active. Each channel maps to a
separate <video> element loaded as local file.
loop schedule (approximate):
phase 0: initial dark (no video) ~2–4s
phase 1: channel active, loop variable
phase 2: glitch transition → dark ~0.5s
phase 3: dark pause ~1–2s
phase 4: next channel loads, glitch in ~0.5s
glitch levels applied to video:
.video-glitch → hue-rotate ±10°, brightness ±25%
contrast ×1.4, scale ±2%, shift ±2px
.video-glitch-extreme → hue-rotate ±20°, brightness ±50%
contrast ×1.9, scale ±4%, shift ±4px
seismic influence on video:
glitch probability and severity scale with getSeismicGlitchLevel()
→ same fragility value f drives video degradation
at f ≥ 0.8: extreme glitch applied to transition
at f ≥ 0.55: standard glitch applied
noise overlay:
canvas drawn over video, randomized per frame during transitions
white noise at variable opacity (0.2 – 0.6)
duration: 250ms – 500ms per transition
10 DATA SOURCES
SEISMIC DATA
network: OV — INGV Osservatorio Vesuviano
station: CSFT (Solfatara, Campi Flegrei, Naples)
date: 20/05/2024
format: MiniSEED → JSON (custom conversion)
channels: HHZ HHN HHE (100 samples/s)
converter: https://φαινόμενα.com/mseed_json.html
catalog: https://eida.ingv.it/
DISPLACEMENT DATA
Menoni, S., Galderisi, A., Carrion, D., & Gerosa, C. (2024)
"Cross-Sectoral and Multilevel Dimensions of Risk and
Resilience Management in Urban Areas Enabled by
Geospatial Data Processing"
Sustainability, 16(19), 8712
https://www.mdpi.com/2071-1050/16/19/8712
CAIN Web Service — Conflict Archive on the INternet
https://cain.ulster.ac.uk/issues/housing/docs/nicrc.htm
AUDIO
field recordings: original material recorded in
Alexandra Park, Belfast
May 2025 – November 2025
processing: Web Audio API (browser-native)
no external audio libraries used
11 MINISEED → JSON CONVERSION PIPELINE
Raw seismic data from INGV/EIDA is distributed in MiniSEED
format (.mseed) — a binary standard for seismic time-series.
The system cannot read this directly; a conversion step
produces the JSON structure the installation expects.
─────────────────────────────────────────────────────────────────
WHAT IS MINISEED
─────────────────────────────────────────────────────────────────
MiniSEED (mini-SEED) is the international standard for
archiving and exchanging digital seismic data. Records are
organized in fixed-length blocks (512–8192 bytes each),
each containing a fixed header (48 bytes), optional
blockettes for metadata, and compressed time-series data.
Compression encoding (this installation):
Steim-1 / Steim-2 (primary INGV/EIDA formats)
INT16, INT32, FLOAT32 (also supported)
Source catalog: https://eida.ingv.it/
Station: CSFT — Solfatara, Campi Flegrei
Channels: HHZ HHN HHE (100 samples/s)
─────────────────────────────────────────────────────────────────
CONVERTER TOOL
─────────────────────────────────────────────────────────────────
browser-based, no external dependencies
https://φαινόμενα.com/mseed_json.html
input: .mseed file (drag & drop or file picker)
output: .json file (download after conversion)
pipeline:
1. reads all fixed-length records from the binary file
2. decodes Steim compression or raw integer/float data
3. concatenates all records into one continuous trace
4. runs STA/LTA event detection on the trace
5. estimates magnitude proxy from amplitude vs noise floor
6. exports structured JSON for the installation
─────────────────────────────────────────────────────────────────
STA/LTA EVENT DETECTION
─────────────────────────────────────────────────────────────────
STA = Short-Term Average (local energy, ~signal)
LTA = Long-Term Average (background energy, ~noise)
trigger: STA/LTA ratio exceeds upper threshold
detrigger: STA/LTA ratio falls below lower threshold
min gap: minimum silence interval between events
all three parameters are configurable in the tool interface
─────────────────────────────────────────────────────────────────
OUTPUT JSON STRUCTURE
─────────────────────────────────────────────────────────────────
{
"metadata": {
"network": "OV",
"station": "CSFT",
"channel": "HHZ",
"start_time": "2024-05-20T...",
"sampling_rate": 100
},
"events": [
{
"timestamp": 1716238260.0,
"magnitude": 2.80,
"depth_km": 4.20,
"cluster": 6
},
...
],
"frames": [
{
"timestamp": 1716238260.0,
"amplitude": 0.00234
},
...
]
}
metadata → station identity and timing reference
events → detected seismic events (drives audio modulation)
frames → downsampled amplitude trace (drives display)
PS³ 11 Rosemary Street, Belfast BT1 1QA · opening 21.03.26 · closing 02.04.26
technical dev_ · · Valeria Vito