Repository navigation
Acoustics
Note
Design stage. This page describes planned mechanics from the design document. Not implemented yet — see Roadmap (milestone M11).
In Prompt Critical sound is an information channel about the state of the plant. An experienced player "hears" the station the way a real plant rounds operator does: a change in pump noise means cavitation, a whistle means a leak, clicking means heat-up. The same physics drives vibration diagnostics, bearing wear and predictive maintenance.
- Every sound is a consequence of a physical variable. There are no ambient sounds without a cause. Loudness, pitch and timbre are computed from simulation parameters.
- Sound duplicates instruments; it does not replace them. Everything audible is measurable by instruments, and vice versa. Some faults invisible to normal sensors, such as an early-stage bearing defect, can be found only with a vibrometer or stethoscope.
- The server computes physics; the client synthesizes sound. The server sends a few bytes of emitter state; the client builds the sound from layered samples.
- Accessibility is mandatory. Every sound has a subtitle. Every sound that carries danger information has a visual duplicate.
pc-sim (server, FAST/SLOW) pc-mod (client)
FluidNetwork, HeatStructure, AcousticClient
PumpDrive, Turbine, Cascade --> AcousticField.stepFast() --> EmitterVoice per emitter:
EmitterState[] (SoA) layers: base / modulation / one-shots
VibrationSpectrum[] Occlusion (rays through blocks, 4 Hz)
SimEvent.AcousticOneShot GeigerSynth (dosimeter clicks)
Subtitles / VisualCues
AcousticSyncService -- AcousticStatePayload (4 Hz, deltas)
-- AcousticOneShotPayload (on event)
AcousticField (domain, sim.acoustics) computes for each emitter its sound power level L_W (dB), pitch multiplier φ, roughness r ∈ [0, 1] (broadband share) and active layer mask, about 16 bytes per emitter with no allocations. VibrationSpectrum holds vibration velocity amplitudes per diagnostic band for rotating equipment.
Level at the listener for a point source in free field:
L_p(d) = L_W − 20·log10(d / 1 m) − 11 − Σ TL_i [dB]
The client does not trust Minecraft's linear attenuation. Each voice plays non-attenuated with a volume the client computes itself: g = clamp(10^((L_p − 100)/20) · g_user, 0, 1). The reference level is acoustics.reference_spl_db = 100. A ~1 MW pump at 2 m (≈ 83 dB) is clearly audible but not at full volume; a water hammer (> 120 dB) saturates. Voices below 30 dB are culled.
Block sound insulation (per block on the emitter–listener ray, capped at acoustics.max_occlusion_db = 45 so a faint hum through walls remains):
| Material | TL per block, dB |
|---|---|
| Air / plants / glass panes | 0 / 2 / 4 |
| Glass block | 12 |
| Wood | 15 |
| Stone, brick | 22 |
Concrete (containment_wall, MC concrete) |
25 |
| Metal (iron blocks, casings) | 18, plus structure-borne path |
| Water (source) | 20 |
Structure-borne sound in pipes. Water hammer, cavitation and leak sounds are re-radiated by pipe / pipe_large blocks of the same hydraulic network within 24 m, at L_W − 10 − 0.5·s dB (s = path length along the pipe). Only the one pipe point nearest the player per network sounds.
Engine limits. Minecraft pitch is clamped to 0.5–2.0, so wide-range sources such as the turbine use 3 octave samples with crossfade. The mod limits itself to acoustics.max_voices = 24 voices, prioritized by level at the listener. All samples are short loops (≤ 4 s).
Cavitation intensity index κ ∈ [0, 1]: 0 is silent, ≈ 0.67 corresponds to NPSH_a = NPSH_r (−3 % head), 1 is fully developed cavitation. Head drop is ΔH/H = min(1, 0.03·exp((NPSH_r − NPSH_a)/(0.15·NPSH_r))): 3 % at NPSH_a = NPSH_r, 22 % at 0.7·NPSH_r, total loss at ≈ 0.47·NPSH_r.
Impeller erosion: dW_imp/dt = κ³ / 200 h (acoustics.cavitation_erosion_hours). Wear lowers head by 0.25·W and raises required NPSH by 50 %·W, so a worn impeller cavitates more readily (positive feedback).
The cavitation sound is a broadband "gravel in the pump" layer (1–20 kHz): L_W,cav = L_W,pump + 15κ − 6. At κ → 1 it is 9 dB louder than the pump itself and impossible to miss; at small κ it is audible only up close or with the stethoscope.
Back-of-envelope examples:
- Main coolant pump at rated conditions (15.5 MPa, 290 °C): NPSH_a ≈ 1101 m, so cavitation is impossible in normal operation. It appears only on loss of primary pressure. At 7.8 MPa the margin is 49 m, and the change in pump sound becomes the first audible sign of boiling. With a small primary leak, the operator hears the pump before some alarms fire.
- Condensate pump (condenser at 5 kPa, condensate at saturation): all NPSH comes from the liquid column. With NPSH_r = 2.5 m and 0.5 m suction losses, the pump must sit at least 3 blocks below the hotwell level or it cavitates from the first start.
Network class: S = emitter state (loop, 4 Hz deltas), E = one-shot event, C = synthesized entirely on the client.
Sound ID (promptcritical:<id>) |
Source | Driven by | L_W, dB | Class |
|---|---|---|---|---|
pump.run |
any pump | ω, shaft power | 70 + 10·log10(P/1 kW) + off-BEP term | S |
pump.cavitation |
pump | κ | see above | S |
pump.bearing |
pump | bearing wear, ω | see bearings | S |
pipe.hammer |
flow link | Δp | 120 + 20·log10(Δp/1 MPa) | E |
pipe.boil.subcooled / .nucleate / .film
|
heat structure | boiling regime, heat flux | — | S |
pipe.thermal_click |
heat structure | dT/dt | 75 | E (bursts) |
pipe.singing |
dead-leg branch | flow velocity, branch length | — | S |
pipe.leak |
leak | leak flow, jet velocity | — | S |
valve.relief.pop / .blow / .chatter
|
relief valve / PORV | lift, flow, chatter | 125 / jet / 115 | E / S / S |
valve.stroke, valve.torque_seat
|
MOV / gate valve | travel, seating | 70 / 85 | S / E |
rod.drive, rod.scram, rod.dashpot
|
rod drives | speed, drop, dashpot impact | 72 / 110 / 100 | S / E / E |
turbine.run, turbine.overspeed, turbine.trip
|
turbine | ω, P | 80 + 10·log10(P/1 MW); +6 dB above 105 %; 120 | S / S / E |
generator.hum |
generator | electrical frequency, load | 70 + 10·log10(1 + 9·P/P_nom) | S |
centrifuge.run, .resonance, .crash
|
cascade | ω, resonance amplitude | 70 + 10·log10(N); 70–100; 125 | S / S / E |
geiger.click |
player's dosimeter / monitor | observed count rate | client | C |
alarm.p1 / p2 / p3, alarm.return
|
alarms | priority | 95 / 90 / 85, 80 | E |
alarm.evacuation |
CAAS / containment | — | 110 | S |
alarm.scram_horn |
scram | — | 100 | E |
containment.creak |
containment at p > 0.8 P_design | p / P_design | 80–100 | E (bursts) |
h2.recombiner |
passive recombiner | reaction rate | 65 | S |
Event levels are of the real order (main steam relief valve ≥ 120 dB at 1 m, water hammer 110–140 dB) and live in assets/promptcritical/acoustics/emitters.json, which a resource pack can override.
-
Fast closure (closing time < 2L/a), Joukowsky:
Δp = ρ·a·Δv, with the wave speed in an elastic pipe from the Korteweg formula. -
Slow closure, Michaud:
Δp ≈ 2ρ·L·Δv / T_c.
Example: feedwater line at 220 °C, 7 MPa, D = 0.3 m, wall 0.02 m, v = 3 m/s, L = 50 m (a = 1222 m/s). A check valve slamming in one tick gives 3.1 MPa; a gate valve closing over 2 s gives 0.13 MPa. The same pipe with cold water (20 °C) gives 4.1 MPa.
In two-phase mixtures the wave speed drops sharply (Wood's formula: at 7 MPa saturation, 735 m/s at 1 % void, 211 m/s at 50 %). But condensation-induced water hammer, when a steam bubble collapses in subcooled water, accelerates a liquid slug to tens of m/s and produces the strongest blows. It is a classic cause of steam line damage during warm-up.
The 0.05 s hydraulic step cannot resolve acoustic waves, so water hammer is an analytical estimate per event, triggered by valve closure, check valve slam on flow reversal, steam void collapse (void > 0.2 → < 0.01 within 2 ticks at > 10 K subcooling) or pump start into a drained line. The result is SimEvent.WaterHammer(linkId, deltaP, position); pipe strength and consequences are handled by the accident models. No event is generated below acoustics.hammer_min_dp_pa = 2e5 (0.2 MPa).
Gas centrifuge rotors are supercritical: they must pass through one or two bending critical speeds during spin-up. With damping ζ = 0.02 the resonance gain is 25, and amplitude builds with time constant 1/(ζ·ω_c), so a fast pass does not let it develop. With the default crash clearance of 12 × the unbalance response, a machine crashes if it accelerates through resonance slower than 1.22·10⁻³·ω_c² (12 rad/s² for ω_c = 100 rad/s). pc_cascade.startRamp(target, rate) must jump the resonance zone. The centrifuge.resonance howl rises at ω_c as a warning. A cascade of hundreds of machines sounds as one emitter; individual voices exist only for machines in resonance or failure (≤ 4 at a time).
Turbine noise combines blade-pass frequency and broadband steam noise, with 3 samples (0–40 %, 40–80 %, 80–120 % speed). The generator has a magnetostriction hum at twice the electrical frequency (100 Hz for a 2-pole machine at 3000 rpm), louder with load. Above 105 % speed a "howl" layer adds +6 dB; at the overspeed trip (110–112 %) the stop valve slams and a long coast-down follows.
Clicks are synthesized on the client from the observed count rate the server sends (1 Hz). The Geiger-Müller model is paralyzable (dead time 190 µs, sensitivity 2.9 s⁻¹ per µSv/h, background 0.2 s⁻¹), so in a strong field the counter chokes and goes quiet. Its maximum is ≈ 1936 s⁻¹ at a true ≈ 1.8 mSv/h. That lesson has to be audible. Clicks form a Poisson process with irregular intervals, like a real counter.
| Observed rate, s⁻¹ | Rendering | Heard as |
|---|---|---|
| < 30 | Individual 20 ms clicks, ±10 % pitch | Sparse crackle |
| 30–300 | Dense crackle loop + clicks | Continuous crackle |
| > 300 | Hiss loop, pitch rising with log rate | Hiss |
| Past the maximum | Hiss fades with observed rate | Alarming "silence" |
Only the dosimeter's owner hears it. Accessibility modes: TONE (pitch ∝ log dose rate, no fold-over) and VISUAL.
| Band | Frequency | Indicates |
|---|---|---|
1x |
f_r (running speed) | Unbalance |
2x |
2·f_r | Misalignment, looseness |
BPF |
z·f_r (z = vanes) | Hydraulic pulsation, off-BEP operation |
BPFO |
(n/2)·f_r·(1 − (d/D)·cos φ) | Outer race defect |
BPFI |
(n/2)·f_r·(1 + (d/D)·cos φ) | Inner race defect |
BSF |
(D/2d)·f_r·(1 − ((d/D)·cos φ)²) | Rolling element defect |
FTF |
(f_r/2)·(1 − (d/D)·cos φ) | Cage defect |
HF |
5–20 kHz acceleration envelope | Early bearing defect, cavitation |
Example: n = 10 rolling elements, d/D = 0.2 at 1000 rpm gives BPFO = 66.7 Hz and BPFI = 100 Hz. Defaults put a healthy large pump at 1.27 mm/s overall, zone A of class III.
Causes of rising amplitudes: impeller erosion and deposits (1x), thermal growth after cold alignment (2x), off-BEP operation (BPF), bearing wear (bearing bands, HF, later 1x), loosening from a nearby explosion (SEISMIC: harmonics, broadband).
| Machine class | A/B | B/C | C/D | In game |
|---|---|---|---|---|
| I (small, ≤ 15 kW) | 0.71 | 1.8 | 4.5 | Auxiliary pumps, valve actuators |
| II (medium, 15–75 kW) | 1.12 | 2.8 | 7.1 | Makeup pumps |
| III (large, rigid foundation) | 1.8 | 4.5 | 11.2 | Main coolant pumps, feedwater pumps |
| IV (large, flexible foundation) | 2.8 | 7.1 | 18.0 | Turbine-generator |
Zone A is a new machine, B unrestricted operation, C restricted (plan repair), D dangerous (stop). Vibration protection: pumps and turbines trip in hardware when overall vibration exceeds the C/D boundary × 1.25 for more than 5 s. A main coolant pump trip then produces a LOW_COOLANT_FLOW scram, a cascade the player must anticipate.
Bearing life follows ISO 281 (L10 = (C/P)^p revolutions, p = 3 ball / 10/3 roller), with a default rated life of 50 000 h. Load grows with unbalance and cavitation. Damage accumulates by the Palmgren-Miner rule in the SLOW domain. Each installed bearing gets an individual Weibull life multiplier (shape 1.5); lubrication factor is 1 normally, 0.2 when overheated (> 363 K) or with overdue oil, 0.05 when running dry. With acoustics.bearing_wear_multiplier = 20, a bearing lasts about 35 hours of real play, enough for a few repairs over a long playthrough.
| Stage | Damage D_b | Symptoms | Detected by |
|---|---|---|---|
| 0 | < 0.6 | — | — |
| 1 | 0.6–0.8 | HF rises ×2…×20 | Stethoscope, vib.hf
|
| 2 | 0.8–0.9 | BPFO/BPFI peaks 0.5–1 mm/s | Vibrometer spectrum, vib.bpfo/bpfi
|
| 3 | 0.9–0.97 | Harmonics, sidebands; pump.bearing audible; bearing +10 K |
Ear, bearing thermometer |
| 4 | 0.97–1.0 | Broadband noise, 1x rises, bearing +30 K, zone D | Everything, including protection |
| Failure | ≥ 1 | Seizure: shaft stops instantly | — |
From stage 1 to failure takes 40 % of life (the P-F interval), enough time to plan a repair. A main coolant pump seizure is the design-basis "locked rotor" accident (SimEvent.PumpSeized). Bearing damage is never published as a tag; you estimate it from symptoms.
| Maintenance | Item / condition | Effect | Time |
|---|---|---|---|
| Oil change |
lubricant, running or stopped |
Lubrication factor → 1 for 2000 physics h | 10 s |
| Bearing replacement |
bearing_kit, pump stopped, clutch open, MAINTENANCE
|
Damage → 0, new life multiplier | 60 s |
| Balancing | vibrometer + balancing_weights, running at rated speed |
Unbalance → 20 % | 3 trial-weight runs of 10 s |
| Alignment |
alignment_tool, pump stopped hot
|
Misalignment → 0.05 mm | 30 s |
| Impeller replacement |
impeller_kit, stopped |
Erosion → 0 | 120 s |
A main coolant pump bearing inside containment cannot be changed at power, and single-loop operation limits power, so "fix now or run to the outage" is a real economic decision.

-
Hand vibrometer (
vibrometeritem): right-click a machine to see overall level, ISO zone with machine class, an 8-band bar spectrum (1x, 2x, BPF, BPFO, BPFI, BSF, FTF, HF), running frequency and bearing housing temperature (IR pyrometer). One reading per second, and the last 32 readings per machine are stored on the item for route-based diagnostics. Accuracy class 5 %. -
Mechanic's stethoscope (
mechanic_stethoscope): while holding right-click on a machine or pipe, all other voices drop by 20 dB and the target'sFAULTandHFlayers rise by 15 dB. Inaudible stage 1–2 bearing noise, incipient cavitation and structure-borne leak noise become audible. -
instrument_vibrationmounts against a machine and publishes vibration tags throughpc_sensor. -
instrument_acousticmounts on a pipe and reports leak and cavitation indices. A pair on the same network, distance L apart, locates a leak by arrival-time difference:x = (L + c_s·Δt) / 2, accurate to about ±1.4 m in water.
| Tag template | Description | Unit | Scale |
|---|---|---|---|
<equip>.vib.overall |
RMS velocity 10–1000 Hz | m/s | 0–0.02 |
<equip>.vib.1x, .2x, .bpf
|
Band amplitudes | m/s | 0–0.02 |
<equip>.vib.bpfo, .bpfi, .bsf
|
Bearing bands | m/s | 0–0.01 |
<equip>.vib.hf |
Acceleration envelope 5–20 kHz | m/s² | 0–50 |
<equip>.vib.peak_freq |
Frequency of largest component | Hz | 0–2000 |
<equip>.bearing.temp |
Bearing temperature | K | 273–423 |
<equip>.speed |
Speed from vibration signal | rad/s | 0–1.2·ω_n |
<pipe>.acoustic.level |
Structure-borne RMS sound pressure | Pa | 0–200 |
<pipe>.acoustic.cavitation |
Cavitation index | 1 | 0–1 |
<pipe>.acoustic.leak |
Leak index | 1 | 0–1 |
<pipe>.acoustic.leak_position |
Distance to leak from sensor 1 (pair) | m | 0–L |
| Packet | Direction | When | Size |
|---|---|---|---|
AcousticStatePayload |
S→C | 4 Hz, only changed emitters within 48 m | ~4.5 B per emitter |
AcousticOneShotPayload |
S→C | On event | ~18 B |
EmitterRemovedPayload |
S→C | On event | ~2 B |
An emitter is sent only if its level changed by ≥ 1 dB or its pitch by ≈ 1 %. A typical machine hall (~20 emitters) costs about 230 B/s, worst case 480 B/s. The acoustics budget is net.budget.acoustics = 400 B/s out of 2 KB/s per player; above it, emitters beyond 16 m drop to 2 Hz. One-shots are limited to 10/s per player. Dosimeter clicks, alarm sounds, modulation and randomization are not sent; the client derives them from data it already has.
Every sound event has a subtitle key subtitles.promptcritical.<id> (ru_ru and en_us). Urgent sounds (alarm.*, pipe.hammer, centrifuge.crash, turbine.overspeed, alarm.evacuation) get a "⚠" prefix. Client settings in promptcritical-client.toml:
| Key | Default | Purpose |
|---|---|---|
acoustics.visual_cues |
true |
Screen-edge indicator toward loud or alarm sounds (colour = priority, shape = type) |
acoustics.screen_shake |
true |
Camera shake ≤ 0.3 s on water hammer or explosion |
acoustics.dosimeter_mode |
CLICKS |
CLICKS / TONE / VISUAL
|
acoustics.max_loudness |
1.0 | Limiter for all mod sounds |
acoustics.alarm_volume |
1.0 | Separate alarm volume |
acoustics.reference_spl_db |
100 | dB level mapped to full volume |
acoustics.max_voices |
24 | Voice limit |
- Create-Integration — pumps, turbine and centrifuge mechanics behind the sounds
-
CC-Tweaked-API —
pc_sensor,getVibrationSpectrum, events - Radiation-Safety — dosimeter model
- Accidents — water hammer damage, locked rotor, LOCA
- SCADA-and-HMI — alarm sounds and vibration alarms
- Architecture — network budget
Prompt Critical · © 2026 denfry · Repository · Report an issue
Start here
Design
Nuclear
Industries
Control and automation
World and server
Development