# Loudspeaker
## Microsim
<iframe src="https://editor.p5js.org/sciencenibber/full/MOYGtBLpj" width="100%" height="620" frameborder="0" sandbox="allow-scripts allow-same-origin"></iframe>
<img src="../SPINTRONICS Images/Loudspeaker.png" alt="Loudspeaker microsim">
*Live sketch: [open in the p5.js editor](https://editor.p5js.org/sciencenibber/sketches/MOYGtBLpj). The poster image above is a placeholder pending an attended or server-side canvas capture.*
### p5.js source
```js
// =====================================================================
// Loudspeaker - Wikitube MicroSim (SPINTRONICS hub, branch T - Transducers)
// Slug/ARTICLE: "Loudspeaker" -> en.wikitube.io/wiki/Loudspeaker
// ---------------------------------------------------------------------
// CONCEPT
// A loudspeaker is an electric-to-acoustic transducer - the exact DUAL
// of the Microphone. Its heart is a CONE driven by an electrodynamic
// MOTOR: a voice coil of length L in a radial field B feels the force
// F = B*L*i = Bl*i (Bl = force factor / motor constant)
// and a moving coil also makes a back-EMF Bl*v that damps it.
// The cone is a DRIVEN, DAMPED HARMONIC OSCILLATOR:
// Mms*x'' + Rms*x' + (1/Cms)*x = Bl*i
// Two derived constants summarise the resonance:
// fs = (1/2pi)*sqrt(1/(Mms*Cms)) free-air cone resonance
// Qts = Qms*Qes/(Qms+Qes) total (Thiele-Small) Q
// with the motor setting the electrical damping Qes = (Re/Bl^2)*sqrt(Mms/Cms).
//
// With r = f/fs and the shared resonance denominator
// D(r) = 1/sqrt( (1-r^2)^2 + (r/Qts)^2 ),
// the two physical read-outs are:
// Excursion (displacement x): X(r) = D(r) low-pass (flat below fs)
// Radiated SPL (acceleration): S(r) = r^2 * D(r) high-pass (flat ABOVE fs)
// Far-field on-axis pressure ~ cone (volume) ACCELERATION, hence the r^2:
// that single factor flips the microphone's low-pass into a loudspeaker
// HIGH-PASS whose flat "piston band" above fs is the usable range, while
// the cone's biggest MOTION sits below fs where it radiates least.
// Phases mirror the mic, reflected: excursion lags (0 -> -90 -> -180 deg),
// radiated SPL leads (+180 -> +90 -> 0 deg).
//
// A-V PATTERN: composition on a driven damped oscillator + a motor -
// * LEFT : Bode magnitude 20*log10(M) vs log f (chart/H) + live dot
// + a faint comparison curve for the OTHER output.
// * R-TOP : moving-coil driver cross-section (B/G) - magnet + pole gap,
// voice coil carrying i, spider + surround, an animated cone;
// a motor-force arrow F=Bl*i and a radiated wavefront whose
// strength tracks the cone ACCELERATION.
// * R-BOT : scope (H) of drive voltage e(t) vs the selected output.
//
// GOLDEN-RULES COMPLIANCE
// * createCanvas(720,520) + pixelDensity(2); layout from width/height.
// * Controls use real symbols + meaningful ranges; Reset restores ALL state.
// * HUD watermark drawn last: title | URL | hints | live equation footer.
// * ASCII only in every string/text(); Unicode (pi, theta, ->) only in comments.
// * Frame-rate independent: dt = min(deltaTime/1000, 0.05). The model is
// closed-form steady-state (not an energy-conserving ODE) -> Verlet N/A.
// * Default noLoop()+redraw() (input-driven); Run toggles a LIGHT loop().
// Per-frame work = a few hundred polyline vertices; static scaffolding
// baked once into an offscreen buffer; setup() cheap. No per-pixel /
// per-atom inner loops -> does not trip the editor loop-protect.
// * Avoided p5 reserved names (incl. methods: mag/map/scale/split/noise):
// helpers are magOut/dbOut/DofR/Hri/thetaOut/xFromF/yFromDB/l10; mode
// var is outMode.
// * p5.disableFriendlyErrors = true; no allocation inside draw().
// =====================================================================
p5.disableFriendlyErrors = true; // quiet the FES (perf + clean console)
const ARTICLE = "Loudspeaker"; // single source of truth (HUD/URL/save)
const WIKI = "en.wikitube.io/wiki/Loudspeaker";
// ---- frequency + dB axes for the response plot (kept fixed; no rescaling) ---
const FMIN = 20, FMAX = 20000; // log frequency axis: 20 Hz .. 20 kHz
const DBMIN = -40, DBMAX = 24; // magnitude axis in dB (each output ref 0 dB at its asymptote)
// ---- scope full-scales (fixed) ----------------------------------------------
const EAXIS = 22; // drive-voltage axis: e in [-22, +22] V
const PAXIS = 3.0; // SPL-mode output axis: normalized pressure [-3, +3]
const XAXIS_MM = 10; // excursion-mode output axis: x in [-10, +10] mm
const XMAX_MM = 8; // illustrative Xmax (cone bottoms out beyond this)
// ---- drive + display scaling ------------------------------------------------
const EREF = 2.83; // reference drive (2.83 Vrms = 1 W into 8 ohm)
const SENS = 88; // nominal passband sensitivity (dB SPL @ 1 m, 2.83 V)
const XREF_MM = 1.6; // illustrative cone excursion amplitude at EREF, DC (mm)
const DISP_PX = 26; // px of drawn cone travel per unit displacement
const DISP_MAX = 26; // clamp drawn cone travel to the panel
// ---- layout (all derived from the 720x520 canvas; no magic coords in draw) --
const PTOP = 84; // panels top
const FOOTY = 326; // y where the live-equation footer sits
// LEFT panel: frequency response (Bode magnitude)
const LX0 = 52, LX1 = 348, LY0 = PTOP, LY1 = 322;
// RIGHT column shared x; split into cross-section (top) + scope (bottom)
const RX0 = 396, RX1 = 704;
const RT0 = PTOP, RT1 = 198; // cross-section sub-panel y-range
const RB0 = 208, RB1 = 322; // scope sub-panel y-range
// ---- control defaults (also used by Reset) ----------------------------------
// f and fs sliders hold log10(Hz); outMode: 0=SPL (accel), 1=excursion (displ).
const DEF = {
f: l10c(800), // 2.903 -> 800 Hz (well into the piston band of a woofer)
fs: l10c(60), // 1.778 -> 60 Hz (typical woofer free-air resonance)
Q: 0.707, // maximally flat (Butterworth) total Q - no peak
E: EREF, // 2.83 V reference drive
mode: 0 // SPL (acceleration)
};
// ---- DOM controls -----------------------------------------------------------
let sldF, sldFs, sldQ, sldE; // four sliders
let btnMode, btnRun, btnReset; // three buttons
// ---- animation + mode state -------------------------------------------------
let running = false; // paused by default (noLoop)
let tsec = 0; // elapsed model time (s), advanced by dt
let outMode = 0; // 0 = SPL (accel), 1 = excursion (displ)
// ---- baked static scenery ---------------------------------------------------
let scene; // p5.Graphics drawn once in setup()
// ASCII-safe base-10 log usable at top level (Math.*) AND in draw -------------
function l10c(x) { return Math.log(x) / Math.LN10; } // for const init (top level)
function l10(x) { return Math.log(x) / Math.LN10; } // alias used in helpers
// =====================================================================
// model (pure functions - no p5 state; mirrored by the node math harness)
// =====================================================================
function DofR(r, Q) { // shared resonance magnitude (excursion)
const a = 1 - r * r;
return 1 / Math.sqrt(a * a + (r / Q) * (r / Q));
}
function magOut(r, Q, mode) { // response magnitude for the selected output
const d = DofR(r, Q);
return (mode === 0) ? r * r * d : d; // SPL ~ acceleration (r^2*D); excursion ~ x (D)
}
function dbOut(r, Q, mode) { // response in dB (each ref 0 dB at its asymptote)
return 20 * l10(Math.max(magOut(r, Q, mode), 1e-6));
}
// complex transfer function H = num/denom so the phase is exact.
// denom = (1 - r^2) + j(r/Q)
// SPL (acceleration): num = -r^2 (2nd-order high-pass)
// excursion (displ.): num = 1 (2nd-order low-pass)
function Hri(r, Q, mode) {
const dre = 1 - r * r, dim = r / Q;
const den = dre * dre + dim * dim || 1e-12;
const nre = (mode === 0) ? -r * r : 1;
// (nre + j*0) * conj(denom) / |denom|^2
return { re: (nre * dre) / den, im: (-nre * dim) / den };
}
function thetaOut(r, Q, mode) { // output phase relative to drive voltage (rad)
const h = Hri(r, Q, mode);
return Math.atan2(h.im, h.re);
}
// =====================================================================
// coordinate maps (use p5 map(); these names do NOT shadow p5)
// =====================================================================
function xFromF(fHz) { return map(l10(fHz), l10(FMIN), l10(FMAX), LX0 + 34, LX1 - 8); }
function yFromDB(db) { return map(db, DBMIN, DBMAX, LY1 - 8, LY0 + 18); }
function scopeY(top, bot, v, vmax) { return map(v, -vmax, vmax, bot - 6, top + 16); }
function scopeT(t, t0, win) { return map(t, t0, t0 + win, RX0 + 8, RX1 - 6); }
// =====================================================================
// setup
// =====================================================================
function setup() {
createCanvas(720, 520);
pixelDensity(2);
textFont("Helvetica");
// -- sliders: (min, max, value, step) --------------------------------------
const w = 132;
// log10(Hz) sliders for f and fs (log axis is the natural one for audio)
sldF = createSlider(l10c(FMIN), l10c(FMAX), DEF.f, 0.01); sldF.position(120, 378); sldF.size(w);
sldFs = createSlider(l10c(20), l10c(500), DEF.fs, 0.01); sldFs.position(120, 416); sldFs.size(w);
sldQ = createSlider(0.2, 2.0, DEF.Q, 0.01); sldQ.position(476, 378); sldQ.size(w);
sldE = createSlider(1.0, 20.0, DEF.E, 0.01); sldE.position(476, 416); sldE.size(w);
// redraw on any slider change so the PAUSED view updates live while dragging
for (const s of [sldF, sldFs, sldQ, sldE]) s.input(redraw);
// -- buttons ----------------------------------------------------------------
btnMode = createButton("Output: SPL (~accel)");
btnMode.position(120, 456); btnMode.size(170, 22);
btnMode.mousePressed(toggleMode);
btnRun = createButton("Run");
btnRun.position(320, 456); btnRun.size(80, 22);
btnRun.mousePressed(toggleRun);
btnReset = createButton("Reset");
btnReset.position(476, 456); btnReset.size(80, 22);
btnReset.mousePressed(resetAll);
buildScene(); // bake panels, axes, gridlines, magnet/housing ONCE
noLoop(); // input-driven by default; Run switches to loop()
}
// =====================================================================
// buildScene - bake everything static into the offscreen buffer once
// =====================================================================
function buildScene() {
scene = createGraphics(720, 520);
const g = scene;
g.pixelDensity(2);
g.background("#0f1420");
g.textFont("Helvetica");
// ---- top caption ----------------------------------------------------------
g.noStroke(); g.fill("#7f8aa3"); g.textSize(9);
g.textAlign(CENTER, TOP);
g.text("drive voltage e(t) -> voice-coil force F=Bl*i -> cone motion -> radiated sound", 360, 26);
// ---- LEFT panel: frequency-response (Bode magnitude) frame + grid ----------
panelFrame(g, LX0, LY0, LX1, LY1, "frequency response |H| (dB) vs log f");
// horizontal dB gridlines + labels
g.textSize(8); g.textAlign(RIGHT, CENTER);
for (let db = DBMIN; db <= DBMAX; db += 8) {
const yy = map(db, DBMIN, DBMAX, LY1 - 8, LY0 + 18);
g.stroke(db === 0 ? "#55708f" : "#202b41"); // 0 dB line emphasised
g.strokeWeight(db === 0 ? 1.3 : 1);
g.line(LX0 + 34, yy, LX1 - 6, yy);
g.noStroke(); g.fill(db === 0 ? "#8fa6c8" : "#66789c");
g.text(db, LX0 + 31, yy);
}
// vertical decade gridlines + labels (log f)
const decades = [20, 100, 1000, 10000, 20000];
const dlabels = ["20", "100", "1k", "10k", "20k"];
g.textAlign(CENTER, TOP);
for (let i = 0; i < decades.length; i++) {
const xx = map(l10(decades[i]), l10(FMIN), l10(FMAX), LX0 + 34, LX1 - 8);
g.stroke("#202b41"); g.strokeWeight(1);
g.line(xx, LY0 + 18, xx, LY1 - 8);
g.noStroke(); g.fill("#66789c");
g.text(dlabels[i], xx, LY1 - 6);
}
// axis titles
g.fill("#9aa6c2"); g.textSize(9);
g.textAlign(LEFT, TOP); g.text("dB", LX0 + 6, LY0 + 16);
g.textAlign(RIGHT, BOTTOM); g.text("f (Hz, log)", LX1 - 6, LY1 - 18);
// ---- RIGHT-TOP: driver cross-section frame + fixed magnet/housing ----------
panelFrame(g, RX0, RT0, RX1, RT1, "moving-coil driver F = Bl*i");
// magnet + pole assembly on the RIGHT (back of the driver); gap faces the coil
const mxx = RX1 - 54; // magnet block left edge
const cyy = (RT0 + RT1) / 2 + 4; // driver axis (vertical centre)
g.noStroke();
g.fill("#2b2b3a"); g.rect(mxx, cyy - 34, 44, 68, 4); // magnet body
g.fill("#3a3340"); g.rect(mxx - 16, cyy - 12, 18, 24, 2); // front pole piece
g.stroke("#5b6b88"); g.strokeWeight(1); g.noFill();
g.rect(mxx - 16, cyy - 30, 60, 60); // top/bottom plates frame
g.noStroke(); g.fill("#caa15a"); g.textSize(9); g.textAlign(CENTER, CENTER);
g.text("N", mxx + 22, cyy - 22); g.text("S", mxx + 22, cyy + 22);
// the magnetic GAP where the coil rides (shaded slot)
g.fill(120, 170, 255, 26); g.rect(mxx - 16, cyy - 9, 16, 18);
g.noStroke(); g.fill("#7f8aa3"); g.textSize(8); g.textAlign(RIGHT, TOP);
g.text("magnet + gap", RX1 - 6, RT0 + 18);
// "sound out" label at the open (left/front) side
g.fill("#7f8aa3"); g.textSize(9); g.textAlign(LEFT, CENTER);
g.text("sound out", RX0 + 8, RT0 + 18);
// ---- RIGHT-BOT: scope frame + zero line ------------------------------------
panelFrame(g, RX0, RB0, RX1, RB1, "scope e(t) [cyan] vs output [amber]");
g.stroke("#2b3550"); g.strokeWeight(1);
g.line(RX0 + 8, (RB0 + RB1) / 2 + 6, RX1 - 6, (RB0 + RB1) / 2 + 6); // visual mid
g.noStroke(); g.fill("#9aa6c2"); g.textSize(9); g.textAlign(RIGHT, BOTTOM);
g.text("t (window = 3 periods, scrolling)", RX1 - 4, RB1 - 3);
}
// small helper: panel frame + caption (baked use only)
function panelFrame(g, x0, y0, x1, y1, label) {
g.noFill(); g.stroke("#33405f"); g.strokeWeight(1.2);
g.rect(x0, y0, x1 - x0, y1 - y0, 4);
g.noStroke(); g.fill("#aebbd9"); g.textSize(10);
g.textAlign(LEFT, TOP);
g.text(label, x0 + 6, y0 + 3);
}
// =====================================================================
// draw
// =====================================================================
function draw() {
// -- read every control ONCE into named locals -----------------------------
const fHz = pow(10, sldF.value()); // Hz drive (signal) frequency
const fs = pow(10, sldFs.value()); // Hz driver free-air resonance
const Q = sldQ.value(); // total quality factor Qts
const Edrv = sldE.value(); // V drive amplitude
const r = fHz / fs; // frequency ratio f/fs
// -- derived response quantities (read sliders ONCE; all else derived) ------
const M = magOut(r, Q, outMode); // selected-output magnitude |H|
const dbv = dbOut(r, Q, outMode); // = 20*log10(M), clamped at the floor
const th = thetaOut(r, Q, outMode); // selected-output phase vs drive (rad)
const Dx = DofR(r, Q); // cone displacement magnitude (low-pass)
const thx = thetaOut(r, Q, 1); // displacement phase (drives the animation)
const Sa = r * r * Dx; // acceleration magnitude (high-pass; SPL)
// -- advance model time only while running (frame-rate independent) ---------
if (running) {
const dt = min(deltaTime / 1000, 0.05); // clamp big frame gaps
tsec += dt;
if (tsec > 1e6) tsec = 0; // guard unbounded growth
}
image(scene, 0, 0); // blit baked panels/axes/grid/magnet
drawResponse(fHz, fs, Q, dbv);
drawDriver(fHz, Edrv, Dx, thx, Sa);
drawScope(fHz, Edrv, M, th);
drawHUD(fHz, fs, Q, Edrv, r, M, dbv, th, Dx, Sa);
}
// ---------------------------------------------------------------------
// LEFT panel: response curve(s) + resonance marker + live operating dot
// ---------------------------------------------------------------------
function drawResponse(fHz, fs, Q, dbv) {
// resonance marker: vertical line at fs
const xr = xFromF(constrain(fs, FMIN, FMAX));
stroke(120, 170, 110, 150); strokeWeight(1.2);
drawingContext.setLineDash([4, 4]);
line(xr, LY0 + 18, xr, LY1 - 8);
drawingContext.setLineDash([]);
noStroke(); fill("#9fe6b0"); textSize(9); textAlign(CENTER, TOP);
text("fs", xr, LY0 + 18);
// faint comparison curve = the OTHER output (dashed)
const other = outMode === 0 ? 1 : 0;
stroke(150, 165, 200, 90); strokeWeight(1.2); noFill();
drawingContext.setLineDash([3, 4]);
responseCurve(fs, Q, other);
drawingContext.setLineDash([]);
// selected output curve (solid, bright)
stroke(outMode === 0 ? "#7fb0ff" : "#ffb347"); strokeWeight(2.5); noFill();
responseCurve(fs, Q, outMode);
// live operating point at the drive frequency
const px = xFromF(constrain(fHz, FMIN, FMAX));
const py = yFromDB(constrain(dbv, DBMIN, DBMAX));
stroke(150, 165, 200, 120); strokeWeight(1);
line(px, LY1 - 8, px, py); // drop line to the f axis
noStroke(); fill("#ffffff"); circle(px, py, 8);
}
// trace one response curve across the log-f axis (light: ~180 vertices)
function responseCurve(fs, Q, mode) {
const N = 180;
beginShape();
for (let i = 0; i <= N; i++) {
const fHz = pow(10, map(i, 0, N, l10(FMIN), l10(FMAX)));
const db = dbOut(fHz / fs, Q, mode);
vertex(xFromF(fHz), yFromDB(constrain(db, DBMIN, DBMAX)));
}
endShape();
}
// ---------------------------------------------------------------------
// RIGHT-TOP: animated moving-coil driver (cone + coil + force + wavefront)
// ---------------------------------------------------------------------
function drawDriver(fHz, Edrv, Dx, thx, Sa) {
const cyy = (RT0 + RT1) / 2 + 4; // driver axis
const coilX = RX1 - 86; // resting x of the voice coil (in the gap)
const ph = TWO_PI * fHz * tsec; // current drive phase
// signals: current ~ drive voltage (ignore coil L); motion = displacement -----
const iNow = sin(ph); // drive/current shape (unit)
const lev = Edrv / EREF; // drive level vs reference
const xUnit = Dx * sin(ph + thx); // cone displacement (signed)
const aNow = -Sa * sin(ph + thx); // cone acceleration ~ -x
const dx = constrain(DISP_PX * lev * xUnit, -DISP_MAX, DISP_MAX); // drawn travel
// --- radiated wavefronts on the FRONT (left); brightness tracks |a| ---------
const wf = constrain(120 * abs(aNow), 0, 150);
noFill(); strokeWeight(2);
for (let k = 0; k < 3; k++) {
stroke(95, 200, 255, wf * (1 - k * 0.28));
const rr = 16 + k * 13 + 8 * (aNow >= 0 ? 1 : 0);
arc(coilX - 44 + dx, cyy, rr * 2, rr * 1.7, radians(118), radians(242));
}
// --- the cone: surround rim (front, left) tapering back to the coil ----------
const rimX = coilX - 60 + dx; // cone mouth (front)
const rimH = 30; // half-height of the cone mouth
stroke("#cfe0ff"); strokeWeight(3); noFill();
line(rimX, cyy - rimH, coilX + dx, cyy - 7); // upper cone wall
line(rimX, cyy + rimH, coilX + dx, cyy + 7); // lower cone wall
// surround (compliant roll at the rim) + spider (inner spring) - drawn as springs
stroke("#6f7da0"); strokeWeight(1.5);
drawSpring(rimX, cyy - rimH, rimX, cyy - rimH - 12, 3);
drawSpring(rimX, cyy + rimH, rimX, cyy + rimH + 12, 3);
// --- voice coil (former + windings) riding in the magnet gap -----------------
stroke("#e0a85a"); strokeWeight(2); noFill();
for (let k = -1; k <= 1; k++) ellipse(coilX + dx, cyy + k * 6, 14, 11);
noStroke(); fill("#9aa6c2"); textSize(8); textAlign(CENTER, TOP);
text("voice coil", coilX + dx, cyy + 14);
// --- current arrow through the coil (length pulses with i(t)) ----------------
const aLen = constrain(22 * iNow, -20, 20);
stroke("#5fd0ff"); strokeWeight(2);
line(coilX + dx, cyy - 20, coilX + dx + aLen, cyy - 20);
noStroke(); fill("#5fd0ff");
const dir = aLen >= 0 ? 1 : -1;
triangle(coilX + dx + aLen, cyy - 20, coilX + dx + aLen - 6 * dir, cyy - 23,
coilX + dx + aLen - 6 * dir, cyy - 17);
textSize(8); textAlign(CENTER, BOTTOM); fill("#7fbfe0");
text("i(t)", coilX + dx, cyy - 24);
// --- motor force arrow F = Bl*i on the cone (points the way the cone is pushed)
const F = constrain(30 * iNow, -28, 28);
stroke("#9fe6b0"); strokeWeight(3);
line(coilX + dx, cyy + 22, coilX + dx - F, cyy + 22);
noStroke(); fill("#9fe6b0");
const fdir = (-F) >= 0 ? 1 : -1;
triangle(coilX + dx - F, cyy + 22, coilX + dx - F + 6 * fdir, cyy + 19,
coilX + dx - F + 6 * fdir, cyy + 25);
// rest-position guide for the cone mouth + excursion label --------------------
stroke(150, 165, 200, 90); strokeWeight(1);
drawingContext.setLineDash([2, 3]);
line(coilX - 60, cyy - rimH - 16, coilX - 60, cyy + rimH + 16);
drawingContext.setLineDash([]);
noStroke(); fill("#cfe0ff"); textSize(9); textAlign(CENTER, BOTTOM);
text("cone", rimX, cyy - rimH - 14);
// numeric peak-to-peak excursion (mm), with an Xmax warning -------------------
const xpp = 2 * XREF_MM * lev * Dx;
fill(xpp > 2 * XMAX_MM ? "#ff6b6b" : "#9fe6b0"); textSize(9); textAlign(LEFT, TOP);
text("x_pp = " + nf(xpp, 0, 2) + " mm" + (xpp > 2 * XMAX_MM ? " (> Xmax!)" : ""),
RX0 + 8, RT0 + 32);
}
// a little zig-zag spring between two points (for surround + spider hints)
function drawSpring(x0, y0, x1, y1, turns) {
beginShape(); noFill();
for (let i = 0; i <= turns * 2; i++) {
const t = i / (turns * 2);
const sx = lerp(x0, x1, t) + ((i % 2) ? 4 : -4);
const sy = lerp(y0, y1, t);
vertex(sx, sy);
}
endShape();
}
// ---------------------------------------------------------------------
// RIGHT-BOT: scope - drive voltage e(t) [cyan] vs selected output [amber]
// ---------------------------------------------------------------------
function drawScope(fHz, Edrv, M, th) {
const Twin = 3 / fHz; // 3 periods regardless of f
const t0 = tsec - Twin;
const N = 240; // light sample count
const lev = Edrv / EREF;
// output axis + amplitude depend on the mode (mm for excursion; norm for SPL)
const outMax = (outMode === 0) ? PAXIS : XAXIS_MM;
const outAmp = (outMode === 0) ? (lev * M) // normalized pressure (passband=1 at EREF)
: (XREF_MM * lev * M); // excursion in mm
// input drive voltage e(t) (cyan), fixed Volt axis
stroke("#5fd0ff"); strokeWeight(2); noFill();
beginShape();
for (let i = 0; i <= N; i++) {
const t = t0 + (i / N) * Twin;
const e = Edrv * sin(TWO_PI * fHz * t);
vertex(scopeT(t, t0, Twin), scopeY(RB0, RB1, e, EAXIS));
}
endShape();
// selected output (amber): outAmp*sin(2pi f t + theta), fixed mode axis
stroke("#ffb347"); strokeWeight(2); noFill();
beginShape();
for (let i = 0; i <= N; i++) {
const t = t0 + (i / N) * Twin;
const v = outAmp * sin(TWO_PI * fHz * t + th);
vertex(scopeT(t, t0, Twin), scopeY(RB0, RB1, constrain(v, -outMax, outMax), outMax));
}
endShape();
// dashed clip guides if the output would run off its axis (resonance / Xmax)
if (outAmp > outMax) {
stroke(255, 107, 107, 130); strokeWeight(1);
drawingContext.setLineDash([4, 4]);
line(RX0 + 8, scopeY(RB0, RB1, outMax, outMax), RX1 - 6, scopeY(RB0, RB1, outMax, outMax));
line(RX0 + 8, scopeY(RB0, RB1, -outMax, outMax), RX1 - 6, scopeY(RB0, RB1, -outMax, outMax));
drawingContext.setLineDash([]);
}
// "now" markers at the right edge (newest sample)
const eEnd = Edrv * sin(TWO_PI * fHz * tsec);
const vEnd = constrain(outAmp * sin(TWO_PI * fHz * tsec + th), -outMax, outMax);
noStroke();
fill("#5fd0ff"); circle(RX1 - 6, scopeY(RB0, RB1, constrain(eEnd, -EAXIS, EAXIS), EAXIS), 6);
fill("#ffb347"); circle(RX1 - 6, scopeY(RB0, RB1, vEnd, outMax), 6);
// output-axis caption (units change with the mode)
fill("#9aa6c2"); textSize(8); textAlign(LEFT, TOP);
text(outMode === 0 ? "amber: p(t) ~ accel (norm)" : "amber: x(t) cone excursion (mm)",
RX0 + 8, RB0 + 16);
}
// ---------------------------------------------------------------------
// HUD watermark (drawn LAST): title | URL | hints | live equation footer
// + the control labels/values + a live region flag.
// ---------------------------------------------------------------------
function drawHUD(fHz, fs, Q, Edrv, r, M, dbv, th, Dx, Sa) {
// ---- control labels + live values (next to each slider) ----
noStroke(); textSize(11);
fill("#cfe0ff"); textAlign(LEFT, CENTER);
text("f", 14, 389); text("fs", 14, 427);
text("Qts", 366, 389); text("E", 372, 427);
fill("#9fe6b0"); textAlign(LEFT, CENTER);
text(fmtHz(fHz), 258, 389);
text(fmtHz(fs), 258, 427);
text(nf(Q, 0, 2), 614, 389);
text(nf(Edrv, 0, 2) + " V (" + nf(Edrv * Edrv / 8, 0, 1) + " W/8ohm)", 614, 427);
// ---- HUD part 1: title (top-left) ----
fill("#ffffff"); textSize(13); textAlign(LEFT, TOP);
text("Loudspeaker - the cone is a driven resonator, run in reverse", 12, 8);
// ---- HUD part 2: URL (top-right) ----
fill("#8fa0c8"); textSize(10); textAlign(RIGHT, TOP);
text(WIKI, 708, 10);
// ---- HUD part 3: control hints ----
fill("#7f8aa3"); textSize(9); textAlign(LEFT, CENTER);
text("drag sliders | Output toggles SPL/Excursion | Run/Pause animates | Reset", 120, 496);
// ---- live region flag (where on the response we are) ----
textAlign(RIGHT, CENTER); textSize(10);
let flag, col;
if (abs(r - 1) < 0.12) {
flag = "AT RESONANCE fs (peak ~Qts)"; col = "#ffd27f";
} else if (r < 1) {
flag = (outMode === 0) ? "below fs: SPL rolls off (+12 dB/oct)"
: "below fs: max cone excursion (flat)";
col = "#7fb0ff";
} else {
flag = (outMode === 0) ? "above fs: flat PISTON BAND (usable)"
: "above fs: excursion falls (mass-controlled)";
col = "#ffb347";
}
fill(col); text(flag, 708, 496);
// ---- approximate absolute SPL + excursion read-outs (illustrative) ----
const spl = SENS + 20 * l10(Edrv / EREF) + 20 * l10(Math.max(Sa, 1e-6));
fill("#9aa6c2"); textSize(9); textAlign(LEFT, TOP);
text("approx SPL ~ " + nf(spl, 0, 1) + " dB @ 1 m", LX0 + 6, LY0 - 14);
// ---- HUD part 4: live equation footer (above the control band) ----
const mEq = (outMode === 0) ? "S=r^2*D(r) (SPL ~ acceleration, high-pass)"
: "X=D(r) (excursion ~ displacement, low-pass)";
noStroke(); fill("#0b0f18"); rect(0, FOOTY, width, 28);
fill("#aab6d6"); textSize(11); textAlign(LEFT, CENTER);
text("D(r)=1/sqrt((1-r^2)^2+(r/Qts)^2) " + mEq, 10, FOOTY + 14);
textAlign(RIGHT, CENTER); fill("#cfe0ff");
text("r=" + nf(r, 0, 3) + " |H|=" + nf(dbv, 0, 1) + " dB theta=" +
nf(degrees(th), 0, 0) + " deg", 712, FOOTY + 14);
}
// format a frequency as Hz or kHz (ASCII only)
function fmtHz(fHz) {
return (fHz >= 1000) ? (nf(fHz / 1000, 0, 2) + " kHz") : (nf(fHz, 0, 0) + " Hz");
}
// =====================================================================
// controls
// =====================================================================
function toggleRun() {
running = !running;
btnRun.html(running ? "Pause" : "Run");
if (running) loop(); else { noLoop(); redraw(); }
}
function toggleMode() {
outMode = (outMode === 0) ? 1 : 0;
btnMode.html(outMode === 0 ? "Output: SPL (~accel)" : "Output: Excursion (~displ)");
redraw();
}
function resetAll() {
sldF.value(DEF.f); sldFs.value(DEF.fs);
sldQ.value(DEF.Q); sldE.value(DEF.E);
outMode = DEF.mode;
btnMode.html("Output: SPL (~accel)");
tsec = 0;
running = false; btnRun.html("Run");
noLoop(); redraw();
}
```
<!-- REAL-GENERATIVE-MEDIA:START -->
## The model this MicroSim animates
**The cone is a driven, damped harmonic oscillator with an electrodynamic motor.** Driving the coil with
a tone `e(t) = E*sin(2*pi*f*t)`, the motor force is `F ~ Bl*i`, and in steady state the cone obeys
```
Mms*x'' + Rms*x' + (1/Cms)*x = F = Bl*i
```
Two derived constants summarise the cone resonance - the **free-air resonant frequency** and the
**total quality factor** (which combines the mechanical and the motor/electrical damping):
```
fs = (1/2pi)*sqrt( 1/(Mms*Cms) ) free-air cone resonance
Qms = (1/Rms)*sqrt(Mms/Cms) mechanical Q
Qes = (Re/(Bl*Bl))*sqrt(Mms/Cms) electrical Q (set by the motor Bl)
Qts = Qms*Qes/(Qms + Qes) total Q at resonance
```
`Re` is the voice-coil DC resistance and `Qts` is the famous **Thiele-Small** total-Q parameter. A
strong motor (large `Bl`) makes `Qes` small and so *tightens* `Qts` - the motor brakes the cone
electrically through its own back-EMF.
Writing the frequency ratio `r = f/fs`, the steady-state response splits into the two physical quantities
the toggle selects. Define the shared resonance denominator
```
D(r) = 1 / sqrt( (1 - r^2)^2 + (r/Qts)^2 )
```
then
```
Excursion (cone displacement x): X(r) = D(r) (low-pass; ref: DC value = 0 dB)
Radiated SPL (cone acceleration a): S(r) = r^2 * D(r) (high-pass; ref: passband = 0 dB)
```
The far-field, on-axis sound pressure of a piston radiator is proportional to its **volume
acceleration**, so the SPL response carries the extra factor `r^2`. That factor is everything:
- **Excursion `X(r)`** is a **low-pass**: well below `fs` it is **flat and maximal** (the suspension
compliance controls the motion - this is why a woofer's cone visibly heaves at low frequencies and why
`Xmax` is the limit there), it **peaks ~ Qts** at `r = 1`, then rolls off above `fs` as the mass takes
over.
- **SPL `S(r)`** is a **2nd-order high-pass**: it rises at **+12 dB/octave** below `fs`, **peaks** at
`r = 1` if `Qts > 0.707`, and is **flat** above `fs` - the **piston band**, the loudspeaker's usable
range. `Qts = 1/sqrt(2) ~ 0.707` is the **maximally flat (Butterworth)** alignment with no peak.
The phases mirror the microphone's exactly, but reflected: cone **excursion lags** the drive
(`0 -> -90 deg -> -180 deg` as `f` sweeps up), while radiated **SPL leads** it (`+180 -> +90 -> 0 deg`).
At resonance the excursion lags by `90 deg` and the radiated pressure leads by `90 deg`.
The left panel plots `20*log10` of the selected response against a logarithmic frequency axis (the
standard Bode magnitude view); the chosen output is solid and the other is shown faintly for comparison,
with a live operating dot at the drive frequency `f`. The upper-right panel is a **cross-section** of the
moving-coil driver - magnet and pole gap, the voice coil carrying the current, the spider and surround,
and the cone that visibly moves; an arrow shows the motor force `F = Bl*i` and a pulsing wavefront shows
the radiated sound whose strength tracks the cone **acceleration**. The lower-right panel is a **scope**
of the drive [[Voltage|voltage]] `e(t)` against the selected output, so the resonant amplification and the phase are
read directly off the waveforms.
The **"aha"**: a loudspeaker is the microphone run backwards. The same one-diaphragm resonance that gave
the microphone its response gives the loudspeaker its response - but because sound radiates from
**acceleration**, the curve flips into a high-pass whose flat **piston band** above `fs` is the speaker's
working range, while the cone's largest **motion** lives down below `fs`, exactly where it makes the
*least* sound.
## Links (Wikipedia order)
<!-- injected from _registry/childlinks/Loudspeaker.json (2026-07-30T02:09:12Z) -->
`1939_New_York_World's_Fair` · `Ableton_Live` · `Absorption_(acoustics)` · `Acoustic_Research` · `Acoustic_impedance` · `Acoustic_lobing` · `Acoustic_resonance` · `Acoustic_suspension` · `Acoustic_transmission_line` · `Air_Motion_Transformer` · `Alexander_Graham_Bell` · `Aliasing` · `Alnico` · `Altec_Lansing` · `Altec_Lansing_Duplex` · `Amplifier` · `Analog_recording` · `Anechoic_chamber` · `Audio_(magazine)` · `Audio_Engineering_Society` · `Audio_crossover` · `Audio_engineer` · `Audio_equalization` · `Audio_power` · `Audio_power_amplifier` · `Audio_signal` · `Audiophile` · `Background_music` · `Bakelite` · `Bamboo` · `Bandwidth_extension` · `Bass_amplifier` · `Bass_reflex` · `Bell_Labs` · `Bessel_function` · `Bi-wiring` · `Binaural_recording` · `Binding_post` · `Bit` · `Cabasse_(company)` · `Capacitance` · `Capacitor` · `Carbon_nanotube` · `Cassette_deck` · `Charles_Algernon_Parsons` · `Chiptune` · `Circuit_bending` · `Compact_disc` · `Comparison_of_analog_and_digital_recording` · `Compression_driver` · `Computer-aided_design` · `Computer_speakers` · `Concert` · [[Copper]] · `Damping_factor` · `Degaussing` · `Diaphragm_(acoustics)` · `Diffraction` · `Diffusion_(acoustics)` · `Digital_Audio_Tape` · `Digital_audio` · `Digital_audio_workstation` · `Digital_recording` · [[Digital_signal_processing]] · `Digital_speaker` · `Dipole_speaker` · `Directional_sound` · `Douglas_Shearer` · `Drum_machine` · `Dust_cap` · `Dynamic_range_compression` · `Edgar_Villchur` · `Edward_W._Kellogg` · `Effects_unit` · `Electric_field` · `Electric_generator` · `Electrical_characteristics_of_dynamic_loudspeakers` · `Electrical_impedance` · `Electrical_polarity` · `Electrical_reactance` · `Electrodynamic_speaker_driver` · `Electromagnet` · `Electromagnetic_coil` · `Electronic_music` · `Electronic_musical_instrument` · [[Electronics]] · `Electrostatic_loudspeaker` · `Experimental_musical_instrument` · `Faraday's_law_of_induction` · `FeONIC` · `Ferrofluid` · `Foam` · [[Frequency_response]] · `Full-range_speaker` · `GarageBand` · `Genelec` · `Geometric_terms_of_location` · `Glass_wool` · `Goji_Electronics` · `Guitar_amplifier` · `Guitar_speaker` · `Guitar_tech` · `Hard_disk_recorder` · `Harmonic_oscillator` · `Headphones` · [[Helium]] · `Hemp` · `Henry_Kloss` · `High-end_audio` · `High_fidelity` · `Home_audio` · `Home_cinema` · `Horn_(acoustic)` · `Horn_loudspeaker` · `Hugh_Chisholm` · `IRCAM` · `Impedance_matching` · `Inductance` · `Infrasound` · `Ingeniøren` · `Insertion_loss` · `Instrument_amplifier` · `Isobaric_loudspeaker` · `James_Bullough_Lansing` · `Jay_Pritzker_Pavilion` · `Johann_Philipp_Reis` · `John_Kenneth_Hilliard` · `John_M._Eargle` · `KEF` · `KLH_(company)` · `Kevlar` · `Keyboard_amplifier` · `Kyocera` · `LARES` · `Lejaren_Hiller` · `Lincoln_Walsh` · `Linear_motor` · `List_of_loudspeaker_manufacturers` · `Logic_Pro` · `Loudness` · `Loudspeaker_(disambiguation)` · `Loudspeaker_acoustics` · `Loudspeaker_enclosure` · `Loudspeaker_measurement` · `MIDI` · `MIDI_controller` · `MP3` · `Machine_press` · `Magnavox` · `Magnet` · `Magnetic_field` · `Magnetic_tape` · `Magnetostatic_loudspeaker` · `Magnetostriction` · `Mass` · [[Materials_science]] · `Max_Mathews` · `Media_control_symbols` · `Megaphone` · `Microphone` · `Microphone_preamplifier` · `Mid-range_speaker` · `MiniDisc` · `Mixing_console` · `Mixing_engineer` · `Moving_iron_speaker` · `Multitrack_recording` · `Music_sequencer` · `Music_store` · `Music_technology` · `Music_technology_(electric)` · `Music_technology_(electronic_and_digital)` · `Music_technology_(mechanical)` · `Music_workstation` · `Musical_Electronics_Library` · `NOx` · `Napa,_California` · [[Neodymium]] · `New_Interfaces_for_Musical_Expression` · `OLED` · `Oliver_Lodge` · `Opus_(audio_format)` · `Oskar_Heil` · `Outboard_gear` · `Ozone` · `Parabolic_loudspeaker` · `Pascal_(unit)` · `Passband` · `Pathé` · `Paul_Wilbur_Klipsch` · `Personal_computer` · `Peter_L._Jensen` · `Phase_plug` · `Phonograph` · `Phonograph_record` · `Piezoelectric_speaker` · `Pioneer_Corporation` · `Planephones` · [[Plasma_(physics)]] · `Plasma_speaker` · `Plasmatronics` · `Player_piano` · `Point_source` · `Popular_Electronics` · `Portable_audio_player` · `Power_supply` · `Powered_speakers` · `Professional_Lighting_and_Sound_Association` · `Professional_audio_store` · `Public_address_system` · `Radio_receiver` · `Rare-earth_magnet` · `Re-recording_mixer` · `Record_producer` · `Reel-to-reel_audio_tape_recording` · `Resistor` · `Resonance` · `Reverb_effect` · `Robert_Moog` · `Roll-off` · `Room_acoustics` · `Rotary_woofer` · `Rudy_Bozak` · `Rule_of_thumb` · `STEIM` · `Sampler_(musical_instrument)` · `Scorewriter` · [[Silver]] · `Society_of_Motion_Picture_and_Television_Engineers` · `Software` · `Software_effect_processor` · `Software_synthesizer` · `Solenoid` · `Sonar` · `Sound` · `Sound_baffle` · `Sound_follower` · `Sound_from_ultrasound` · `Sound_module` · `Sound_power` · `Sound_pressure` · `Sound_recording_and_reproduction` · `Sound_reinforcement_system` · `Soundbar` · `Speaker_stands` · `Speaker_terminal` · `Speaker_wire` · `Speakerphone` · `Speech` · `Standing_wave` · `Stereophile` · `Studio_monitor` · `Subwoofer` · `Super_tweeter` · `Surround_sound` · [[Synergy]] · `Synthesizer` · `THX` · `Tannoy` · `Tape_op` · `Tape_recorder` · `Telephone` · `Television` · `Theremin` · `Thermophone` · `Thiele/Small_parameters` · `Thomas_Edison` · `Timbre` · `Timeline_of_audio_formats` · `Trade-off` · [[Transducer]] · `Transmission_line` · `Transmission_line_loudspeaker` · `Tweeter` · `Vehicle_audio` · `Victor_Talking_Machine_Company` · `Voice_coil` · `Watch` · `Waveguide` · [[Wayback_Machine]] · `Wikimedia_Commons` · `Wireless_speaker` · `Woofer`
## From the Real GENERATIVE library

*Loudspeaker — placed from the Real G.E.N.E.R.A.T.I.V.E. course library (Telecommunications room). Source: Wikimedia Commons (via Wikipedia article media). [Details & license](https://commons.wikimedia.org/wiki/File:Electrodynamic-loudspeaker.png).*

*Animated: Loudspeaker — placed from the Real G.E.N.E.R.A.T.I.V.E. course library (Telecommunications room). Source: Wikimedia Commons (via Wikipedia article media). [Details & license](https://commons.wikimedia.org/wiki/File:Es_spk.gif).*
> A loudspeaker (commonly referred to as a speaker or, more fully, a speaker system) is a combination of one or more speaker drivers, an enclosure, and electrical connections (possibly including a crossover network). The speaker driver is an electroacoustic transducer[1]: 597 that converts an electrical audio signal into a corresponding sound.[2] ([Wikipedia](https://en.wikipedia.org/wiki/Loudspeaker))
<!-- REAL-GENERATIVE-MEDIA:END -->
<!-- SIGN-SYSTEMS:START -->
**Semiotic universals** (the notations and alphabet letters this article speaks — each opens its canonical card): frequency · resonance · phase · damping · potential. Index: the glyph gallery · SEMIOTICS PORTAL.
<!-- SIGN-SYSTEMS:END -->
## Media (PD/CC)
<!-- MEDIA-DEPLOY:Loudspeaker/Es_spk.gif -->
!Gif Library/Speaker (audio equipment)/Es spk.gif
*Es_spk.gif · Original uploader was Rohitbd at en.wikipedia · CC BY-SA 3.0 · [source](https://commons.wikimedia.org/wiki/File:Es_spk.gif)*
<!-- /MEDIA-DEPLOY -->
## Overview
A **loudspeaker** is a **[[Transducer|transducer]]** that converts an electrical [[Signal|signal]] back into **sound** - a
travelling pressure variation in air. It is the canonical *output* transducer of the audio chain and the
exact dual of the Microphone: where the microphone's diaphragm *hears*, the loudspeaker's cone
*speaks*. The overwhelmingly common type is the **electrodynamic (moving-coil) driver**, invented in its
modern form by Rice and Kellogg (1925), which works by the same electromagnetic [[Coupling|coupling]] as a dynamic
microphone, run in reverse.
The mechanism has two halves wired together:
- A **motor**. A light cylindrical **voice coil** of wire (total length `L` in the gap) sits in the
strong radial field `B` of a permanent magnet. When the amplifier pushes a current `i` through the
coil, the coil feels a [[Force|force]] given by the **Lorentz / motor law**: `F = B*L*i`. The product `B*L` -
the **force factor** (or motor constant) `Bl`, in tesla-metres or newtons per amp - is the single most
important motor parameter. A moving coil also generates a **back-EMF** `B*L*v` proportional to its
[[Velocity|velocity]] `v`, which feeds back into the circuit and (through the amplifier's low output impedance)
electrically *damps* the cone.
- A **radiator**. The coil is glued to a stiff **cone** (the diaphragm) held centred by two springs - a
**spider** at the coil and a **surround** at the rim. Cone, coil and trapped air together have a moving
**mass** `Mms`, the suspension has a mechanical **compliance** `Cms` (stiffness `k = 1/Cms`), and the
losses give a mechanical **resistance** `Rms`. So the cone is a **mass on a spring with [[Damping|damping]]** - a
driven, damped harmonic oscillator - pushed by the motor force `F = B*L*i`.
This is the same oscillator the microphone MicroSim animates, but driven from the *electrical* side and
read out as *radiated sound*. That single change - from reading the motion to radiating it - flips the
frequency response from a low-pass into a **high-pass**, and that flip is what this MicroSim makes
visible.
## Parameter table (each control -> real symbol + range)
| Control | Symbol | Physical meaning | Range | Default |
|---|---|---|---|---|
| Signal frequency | `f` | frequency of the drive tone into the coil (log-swept slider) | 20 - 20000 Hz | 800 Hz |
| Driver resonance | `fs` | free-air cone resonance `fs = (1/2pi)*sqrt(1/(Mms*Cms))` (log slider) | 20 - 500 Hz | 60 Hz |
| Total Q | `Qts` | Thiele-Small total quality factor `Qts = Qms*Qes/(Qms+Qes)`; `zeta = 1/(2Qts)` | 0.2 - 2.0 | 0.707 |
| Drive voltage | `E` | drive amplitude (`2.83 V = 1 W into 8 ohm`, the standard reference) | 1 - 20 V | 2.83 V |
| Output | mode | **SPL** (V proportional to acceleration, high-pass) vs **Excursion** (proportional to displacement, low-pass) | toggle | SPL |
Derived and shown live in the HUD: frequency ratio `r = f/fs`, response magnitude `|H| = 20*log10(M)` in
dB, output phase `theta` in degrees, an approximate on-axis SPL in dB (referenced to a nominal
sensitivity of `~88 dB @ 1 m, 2.83 V` in the passband), and the cone peak excursion in millimetres
(illustrative scaling). The standard reference uses `2.83 Vrms = 1 W into 8 ohm`.
## Learning objective
Explain why a moving-coil loudspeaker's on-axis frequency response is a **2nd-order high-pass** set by the
cone's mechanical resonance `fs` and total damping `Qts` - radiated **SPL follows cone acceleration**
(flat in the piston band above `fs`, +12 dB/octave roll-off below `fs`, a resonance peak when
`Qts > 0.707`) while cone **excursion follows displacement** (largest and constant well below `fs`) - and
predict how moving `fs`, changing `Qts`, and raising the drive level reshape the **passband, resonance
peak, low-frequency roll-off, phase, and cone excursion**. Recognise the loudspeaker as the radiating
dual of the microphone: the same diaphragm resonance, read out as acceleration instead of displacement.
## A-V pattern
A composition built on a **driven, damped harmonic oscillator with an electrodynamic motor**
(`F = Bl*i`): an **H/chart** Bode magnitude plot of the selected frequency response (left, log-frequency,
with a live operating point and a faint comparison curve for the other output), a **B/G** cross-section
schematic of the moving-coil driver - magnet, pole gap, voice coil, spider, surround and an animated cone
- with a motor-force arrow and a radiated-wavefront cue tied to the cone acceleration (upper right), and
an **H** signals-over-time scope of drive voltage vs the selected output exposing gain and phase
(lower right).
## Sources
- Loudspeaker - Wikipedia: https://en.wikipedia.org/wiki/Loudspeaker
- Moving-coil / dynamic driver; motor force `F = BLi` and back-EMF `BLv`: https://en.wikipedia.org/wiki/Loudspeaker#Moving-coil
- Voice coil (force factor Bl, the motor constant): https://en.wikipedia.org/wiki/Voice_coil
- Thiele/Small parameters (fs, Qms, Qes, Qts, Cms, Mms, Re): https://en.wikipedia.org/wiki/Thiele/Small_parameters
- Driven, damped harmonic oscillator (resonance magnitude D(r), Q, phase): https://en.wikipedia.org/wiki/Harmonic_oscillator#Driven_harmonic_oscillators
- Sound radiation proportional to volume acceleration (piston in the piston band): https://en.wikipedia.org/wiki/Loudspeaker#Driver_design
<!-- CRAFT-LINK:START g12 -->
*Built to the [[WT!P5_js_Microsim_Master_Class|p5.js Master Class]].*
<!-- CRAFT-LINK:END -->
<!-- ACOUSIM:BEGIN g22 — Acoustics portal microsim (framework build, specs/acoustics/sims/Loudspeaker.json); do not hand-edit inside -->
**Microsim — three.js (Wikitube framework):** *Loudspeaker*
<div class="wt-sim" data-src="https://wikitube-3d-microsims.netlify.app/acoustics/Loudspeaker.html" data-title="Loudspeaker"></div>
*Built from `MICROSIM_GUIDE/specs/acoustics/sims/Loudspeaker.json`; part of the [[PORTAL_Acoustics|Acoustics portal]] spine (section sims and See-also variants).*
<!-- ACOUSIM:END -->
## Wikipedia : Wikitube
**Strict pair:** [Wikipedia](https://en.wikipedia.org/wiki/Loudspeaker) : [Wikitube](https://en.wikitube.io/wiki/Loudspeaker)
## Previous hub tags
Tree parent: [[Control_theory]].
Legacy hubs: none.
---
*Sources: 1 legacy note. Minted wave 1, 2026-07-30 (v1.6 order).*