# TECHNICAL SPECIFICATION & OPERATIONAL WORKFLOW MANUAL: OZONE WEB MASTERING SUITE

This document compiles the complete architectural layout, module inventory, control parameter specifications, dynamic re-routing execution matrices, visual metering systems, and full single-file implementation code for the Ozone-Style Web Mastering Engine.

---

## 1. AUDIO PROCESSING MODULES SUMMARY

| Module Name | Core Function | Web Audio API Nodes / Class | Key Control Parameters | Inherited Acoustic Impact |
| --- | --- | --- | --- | --- |
| **Dynamic EQ** | Dynamic frequency filtering and equalization | `BiquadFilterNode` (`lowshelf`, `peaking`, `highshelf`) | 4-Band Gain ($-12 \text{ to } +12\text{ dB}$), Frequency, Q Factor | Balances frequency spectrum, carves out harsh resonances, or boosts air/warmth dynamically without introducing harsh clipping. |
| **Stereo Imager** | Mid/Side spatial expansion and balance | `ChannelSplitterNode`, `ChannelMergerNode`, `GainNode` | 4-Band Width ($-100\% \text{ to } +100\%$), Mid/Side Gain | Expands stereo width across High/Mid bands while collapsing Low/Sub-bass bands to Mono for maximum low-end punch. |
| **IRC IV Maximizer** | True-Peak Limiter & Peak Gain Boosting | `WaveShaperNode` (Soft Clip), `DynamicsCompressorNode` | Boost Gain ($+0 \text{ to } +12\text{ dB}$), Soft Clip %, Upward Comp, Ceiling | Maximizes perceived loudness (LUFS) to commercial distribution standards while suppressing transient spikes below $0\text{ dBFS}$. |
| **Master Utilities** | I/O Management, Metering & Codec Emulation | `AnalyserNode` (FFT 2048), `GainNode` | Delta Listen, Gain Match, Codec Preview, Dither | Enables objective A/B listening tests between pre/post-mastered signals, emulates lossy MP3/AAC compression, and manages bit-depth reduction. |

---

## 2. PARAMETER CONTROL SPECIFICATIONS MATRIX

| Module Group | Parameter Name | Range | Unit | Node Handler / Formula | Technical Purpose |
| --- | --- | --- | --- | --- | --- |
| **Dynamic EQ** | `eqLowGain` | $-12.0 \to +12.0$ | dB | Lowshelf @ $100\text{Hz}$ | Controls low-end punch and sub-bass weight. |
| **Dynamic EQ** | `eqMid1Gain` | $-12.0 \to +12.0$ | dB | Peaking @ $822\text{Hz}$, $Q=0.7$ | Cleans up muddy or boxy low-mid frequencies. |
| **Dynamic EQ** | `eqMid2Gain` | $-12.0 \to +12.0$ | dB | Peaking @ $3.2\text{kHz}$, $Q=1.2$ | Adjusts vocal clarity and instrument presence. |
| **Dynamic EQ** | `eqHighGain` | $-12.0 \to +12.0$ | dB | Highshelf @ $10\text{kHz}$ | Adds high-frequency air, sheen, and sparkle. |
| **Stereo Imager** | `w1` (Band 1) | $-100 \to +100$ | % | Mid/Side Gain ($0-100\text{Hz}$) | Collapses sub-bass to mono ($0\%$) to ensure club system compatibility. |
| **Stereo Imager** | `w2` (Band 2) | $-100 \to +100$ | % | Mid/Side Gain ($100\text{Hz}-1\text{kHz}$) | Balances body and width for core instruments. |
| **Stereo Imager** | `w3` (Band 3) | $-100 \to +100$ | % | Mid/Side Gain ($1\text{kHz}-6\text{kHz}$) | Expands upper-mid imaging for a wider soundstage. |
| **Stereo Imager** | `w4` (Band 4) | $-100 \to +100$ | % | Mid/Side Gain ($6\text{kHz}-20\text{kHz}$) | Widens top-end spatial atmosphere. |
| **Maximizer** | `maxGain` | $0.0 \to +12.0$ | dB | Gain Boost Node | Drives the signal into the limiter to increase overall LUFS. |
| **Maximizer** | `maxUpward` | $0.0 \to +10.0$ | dB | Dynamic Range Expander | Brings up low-level details for a denser mix. |
| **Maximizer** | `maxSoftClip` | $0 \to 100$ | % | Arctan WaveShaper Curve | Rounds off sharp transient peaks to add analog-style saturation. |
| **Maximizer** | `ceiling` | $-6.0 \to 0.0$ | dBFS | Brickwall Limiter Threshold | Absolute ceiling threshold (Default: $-0.10\text{ dBFS}$). |

---

## 3. AUDIONODE GRAPH RE-ROUTING EVENT MATRIX

| UI Action | Triggered Event | Audio Engine Method (`OzoneAudioChainManager`) | Node Graph Execution | Perceived Acoustic Effect |
| --- | --- | --- | --- | --- |
| **Add Module (`[+] Add`)** | Click Add Module button | `addModuleToChain(moduleName)` | Appends module to `moduleChain`, disconnects existing routing, and re-connects all nodes in sequence. | Introduces additional processing step to the signal chain prior to Master Out. |
| **Remove Module (`[x] Remove`)** | Click Remove Module button | `removeModuleFromChain(moduleName)` | Filters module out of `moduleChain`, disconnects old connections, and connects remaining nodes directly. | Bypasses the removed module's processing, reducing latency and CPU load. |
| **Reorder Chain** | Drag & Drop Module Card | `reorderChain(newArray)` | Disconnects the entire graph and rebuilds connections from scratch based on the new array order. | Alters processing order (e.g., Stereo Imager widening prior to EQ vs. post-EQ). |
| **Toggle Module Power** | Click Module Power button | `state.moduleActive = !active; updateAudioParameters();` | Sets target band gains to $0\text{ dB}$ or bypasses signal around module. | Passes signal through the target module without tonal modification (flat response). |
| **Master Bypass** | Click Master Bypass button | `toggleMasterBypass()` | Reroutes input directly: `inputAnalyser` $\to$ `masterBypassGain` $\to$ `outputAnalyser`. | Instantly returns signal to raw, unprocessed dry audio for A/B comparison. |

---

## 4. GRAPHICAL METERS & VISUALIZER CANVAS SYSTEMS

| Visualizer Canvas | Source Audio Node | Rendering Math / Algorithm | Graphical Characteristics & Refresh Rate |
| --- | --- | --- | --- |
| **Dynamic EQ Spectrum** | `outputAnalyser.getByteFrequencyData()` | Logarithmic Frequency Scaling ($20\text{Hz} - 20\text{kHz}$) | Cyan composite EQ curve overlaid across a live 60 FPS FFT frequency spectrum fill. |
| **Polar Vectorscope** | Channel Splitter (Independent L/R Signals) | Polar Coordinate Calculation ($\theta = \arctan(R/L)$, radius $r$) | 180° radial particle scatter plot depicting phase correlation and stereo width. |
| **I/O Stereo VU Meters** | `inputAnalyser` & `outputAnalyser` | Peak Calculation ($dB = 20 \log_{10}(\text{level})$) using Time-Domain data | Vertical Green $\to$ Yellow $\to$ Red gradient meters with real-time numeric dBFS readouts. |

---

## 5. MASTER UTILITIES COMPARISON

| Utility Feature | DSP Mechanism / Formula | Operational Purpose in Mastering |
| --- | --- | --- |
| **Delta Listen (`[i] Delta`)** | $S_{\text{Delta}}(t) = S_{\text{Input}}(t) - S_{\text{Output}}(t)$ | Isolates the exact signal difference being removed or compressed to prevent over-processing. |
| **Gain Match** | $G_{\text{Match}} = \text{LUFS}_{\text{Input}} - \text{LUFS}_{\text{Output}}$ | Automatically matches output loudness to input level to enable objective A/B evaluations free from "louder is better" bias. |
| **Codec Preview** | Low-pass Biquad Filter @ $16\text{kHz}$ | Simulates lossy high-frequency roll-off typical of 128 kbps MP3/AAC encoding. |
| **Dither Processor** | Triangular PDF Noise Injection | Prevents quantization distortion when converting bit-depth from 32-bit Float down to 16-bit PCM. |

---

## 6. FULL SINGLE-FILE IMPLEMENTATION CODE

Below is the complete single-file source code incorporating HTML5, Tailwind CSS, FontAwesome, HTML5 Canvas, and Web Audio API DSP engines modeling the iZotope Ozone mastering interface:

```html
<!DOCTYPE html>
<html lang="en" class="h-full bg-slate-950 text-slate-100 select-none">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Ozone Mastering Studio Engine</title>
  <!-- Tailwind CSS -->
  <script src="https://cdn.tailwindcss.com"></script>
  <!-- Font Awesome -->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
  <!-- Fonts -->
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet">
  
  <style>
    body { font-family: 'Inter', sans-serif; }
    .font-mono { font-family: 'JetBrains Mono', monospace; }
    
    ::-webkit-scrollbar { width: 4px; height: 4px; }
    ::-webkit-scrollbar-track { background: #080b12; }
    ::-webkit-scrollbar-thumb { background: #1e293b; border-radius: 2px; }
    ::-webkit-scrollbar-thumb:hover { background: #334155; }

    .knob-container { touch-action: none; user-select: none; }
    .knob-dial {
      transition: transform 0.05s ease-out;
      box-shadow: inset 0 1px 3px rgba(255,255,255,0.15), 0 3px 8px rgba(0,0,0,0.8);
    }
    
    /* Ozone Theme Dark Gradient Panels */
    .oz-panel {
      background: linear-gradient(180deg, #131822 0%, #0d1118 100%);
      border: 1px solid rgba(255, 255, 255, 0.07);
    }
    .oz-card {
      background: #18202c;
      border: 1px solid rgba(255, 255, 255, 0.1);
    }
    .oz-card-active {
      background: #1e2a3a;
      border-color: #38bdf8;
      box-shadow: 0 0 12px rgba(56, 189, 248, 0.2);
    }
  </style>
</head>
<body class="h-full flex flex-col overflow-hidden bg-slate-950 text-slate-200">

  <!-- TOP TRANSPORT & SESSION BAR -->
  <header class="h-12 bg-slate-900/90 border-b border-slate-800/80 flex items-center justify-between px-4 z-30">
    <div class="flex items-center gap-3">
      <div class="w-7 h-7 rounded-lg bg-cyan-600 flex items-center justify-center font-bold text-white shadow-lg shadow-cyan-950">
        <i class="fa-solid fa-atom text-xs"></i>
      </div>
      <div>
        <h1 class="text-xs font-bold tracking-wider text-white flex items-center gap-2">
          OZONE ADVANCED <span class="text-[9px] bg-cyan-950 text-cyan-400 border border-cyan-800/80 px-1.5 py-0.5 rounded font-mono">WEB MASTERING V10.5</span>
        </h1>
      </div>
    </div>

    <!-- Central Signal Generator & Transport -->
    <div class="flex items-center gap-3 bg-slate-950 px-3 py-1 rounded-lg border border-slate-800/80">
      <button id="playAudioBtn" class="px-3 py-1 bg-cyan-600 hover:bg-cyan-500 text-white rounded text-xs font-semibold flex items-center gap-1.5 transition-all shadow-md shadow-cyan-950">
        <i class="fa-solid fa-play text-[10px]"></i> <span>Play Reference Loop</span>
      </button>
      <button id="stopAudioBtn" class="px-3 py-1 bg-slate-800 hover:bg-slate-700 text-slate-300 rounded text-xs font-semibold flex items-center gap-1.5 transition-all">
        <i class="fa-solid fa-stop text-[10px]"></i> <span>Stop</span>
      </button>

      <div class="h-4 w-[1px] bg-slate-800 mx-1"></div>

      <div class="flex items-center gap-2 text-xs font-mono">
        <span class="text-slate-400 text-[11px]">Preset:</span>
        <select id="masterPresetSelect" class="bg-slate-900 border border-slate-700 rounded px-2 py-0.5 text-xs text-cyan-300 outline-none focus:border-cyan-500">
          <option value="adaptive">Adaptive Dynamic Master</option>
          <option value="edm_club">EDM / Club Punch Maximizer</option>
          <option value="wide_space">Cinematic Stereo Expansion</option>
          <option value="transparent">Transparent High-Clarity Limiter</option>
        </select>
      </div>
    </div>

    <!-- Right Target Meter Info -->
    <div class="flex items-center gap-3 text-xs font-mono">
      <span class="text-slate-400 text-[11px]">Target LUFS:</span>
      <span id="targetLufsText" class="text-cyan-400 font-bold bg-slate-950 border border-cyan-900/60 px-2 py-0.5 rounded">-11.0 LUFS</span>
    </div>
  </header>

  <!-- MODULE RACK STRIP (OZONE UPPER MODULE BAR) -->
  <div class="h-16 bg-slate-950 border-b border-slate-800 px-4 flex items-center gap-2 overflow-x-auto">
    <span class="text-[10px] font-bold text-slate-500 uppercase tracking-widest mr-1 font-mono">MODULE CHAIN:</span>

    <!-- Module 1: Dynamic EQ Card -->
    <div id="cardDynamicEq" class="oz-card oz-card-active w-36 h-12 rounded-lg p-2 flex items-center justify-between cursor-pointer transition-all">
      <div class="flex items-center gap-2">
        <button id="toggleEqBtn" class="w-4 h-4 rounded-full bg-cyan-500 hover:bg-cyan-400 text-slate-950 flex items-center justify-center text-[9px] font-bold">
          <i class="fa-solid fa-power-off"></i>
        </button>
        <div>
          <div class="text-[11px] font-bold text-slate-200">Dynamic EQ</div>
          <div class="text-[9px] text-cyan-400 font-mono">4-Band Peak</div>
        </div>
      </div>
      <i class="fa-solid fa-chart-area text-slate-500 text-xs"></i>
    </div>

    <!-- Module 2: Stereo Imager Card -->
    <div id="cardImager" class="oz-card w-36 h-12 rounded-lg p-2 flex items-center justify-between cursor-pointer transition-all">
      <div class="flex items-center gap-2">
        <button id="toggleImagerBtn" class="w-4 h-4 rounded-full bg-cyan-500 hover:bg-cyan-400 text-slate-950 flex items-center justify-center text-[9px] font-bold">
          <i class="fa-solid fa-power-off"></i>
        </button>
        <div>
          <div class="text-[11px] font-bold text-slate-200">Imager</div>
          <div class="text-[9px] text-slate-400 font-mono">4-Band Width</div>
        </div>
      </div>
      <i class="fa-solid fa-circle-nodes text-slate-500 text-xs"></i>
    </div>

    <!-- Module 3: Maximizer Card -->
    <div id="cardMaximizer" class="oz-card w-36 h-12 rounded-lg p-2 flex items-center justify-between cursor-pointer transition-all">
      <div class="flex items-center gap-2">
        <button id="toggleMaximizerBtn" class="w-4 h-4 rounded-full bg-cyan-500 hover:bg-cyan-400 text-slate-950 flex items-center justify-center text-[9px] font-bold">
          <i class="fa-solid fa-power-off"></i>
        </button>
        <div>
          <div class="text-[11px] font-bold text-slate-200">Maximizer</div>
          <div class="text-[9px] text-slate-400 font-mono">IRC IV True Peak</div>
        </div>
      </div>
      <i class="fa-solid fa-gauge-high text-slate-500 text-xs"></i>
    </div>

    <!-- Empty Add Module Slot -->
    <div class="w-24 h-12 rounded-lg border border-dashed border-slate-800 flex items-center justify-center text-slate-600 hover:text-slate-400 hover:border-slate-600 cursor-pointer transition-all">
      <i class="fa-solid fa-plus text-sm"></i>
    </div>
  </div>

  <!-- MAIN WORKSPACE CONTAINER -->
  <main class="flex-1 flex overflow-hidden">
    
    <!-- LEFT MODULE WORKSPACE (DYNAMIC VISUALIZER & CONTROLS) -->
    <div class="flex-1 flex flex-col bg-slate-950 relative overflow-y-auto">
      
      <!-- SUB HEADER TOOLBAR -->
      <div class="h-9 bg-slate-900/60 border-b border-slate-800/80 px-4 flex items-center justify-between text-xs font-mono">
        <div class="flex items-center gap-3">
          <span class="text-slate-400 flex items-center gap-1.5"><i class="fa-solid fa-headphones text-cyan-400"></i> Delta Listen</span>
          <select id="modeSelect" class="bg-slate-950 border border-slate-800 rounded px-2 py-0.5 text-[11px] text-slate-300 outline-none">
            <option value="irc4">IRC IV - Classic</option>
            <option value="irc3">IRC III - Balanced</option>
            <option value="irc2">IRC II - Crisp</option>
          </select>
        </div>

        <div class="flex items-center gap-2 text-[11px]">
          <span class="text-slate-400">Learn Input Gain:</span>
          <button id="learnGainBtn" class="bg-cyan-950 text-cyan-300 border border-cyan-800/80 px-2 py-0.5 rounded font-bold hover:bg-cyan-900 transition-colors">
            -11.0 LUFS
          </button>
        </div>
      </div>

      <!-- VIEW 1: DYNAMIC EQ VISUALIZER & BAND SETTINGS -->
      <div id="viewDynamicEq" class="flex-1 flex flex-col p-4 gap-4">
        <!-- EQ Canvas Display -->
        <div class="relative w-full h-64 bg-slate-950 border border-slate-800/90 rounded-xl overflow-hidden shadow-inner cursor-crosshair">
          <canvas id="eqCanvas" class="w-full h-full block"></canvas>
        </div>

        <!-- EQ Band Control Panel (Ozone Band Settings Window) -->
        <div class="oz-panel p-3 rounded-xl grid grid-cols-4 gap-3">
          <!-- Band 1 (Low) -->
          <div class="bg-slate-900/80 border border-slate-800 p-2.5 rounded-lg flex flex-col justify-between">
            <div class="flex items-center justify-between text-xs font-bold text-cyan-400">
              <span>BAND 1 (LOW)</span>
              <span class="text-[10px] font-mono text-slate-400">100 Hz</span>
            </div>
            <div class="flex items-center justify-around my-2">
              <div class="knob-container" data-param="eqLowGain" data-min="-12" data-max="12" data-value="1.5" data-unit="dB">
                <div class="knob-dial w-9 h-9 rounded-full bg-slate-800 border-2 border-cyan-500 relative flex items-center justify-center shadow-md">
                  <div class="knob-pointer w-0.5 h-3 bg-cyan-400 rounded absolute top-1"></div>
                </div>
              </div>
              <div class="text-center font-mono">
                <span class="knob-val text-xs text-slate-200 font-bold">+1.5 dB</span>
              </div>
            </div>
            <div class="text-[9px] text-slate-500 font-mono text-center">Shelf Filter</div>
          </div>

          <!-- Band 2 (Mid Low) -->
          <div class="bg-slate-900/80 border border-slate-800 p-2.5 rounded-lg flex flex-col justify-between">
            <div class="flex items-center justify-between text-xs font-bold text-amber-400">
              <span>BAND 2 (MID LOW)</span>
              <span class="text-[10px] font-mono text-slate-400">822 Hz</span>
            </div>
            <div class="flex items-center justify-around my-2">
              <div class="knob-container" data-param="eqMid1Gain" data-min="-12" data-max="12" data-value="-1.0" data-unit="dB">
                <div class="knob-dial w-9 h-9 rounded-full bg-slate-800 border-2 border-amber-500 relative flex items-center justify-center shadow-md">
                  <div class="knob-pointer w-0.5 h-3 bg-amber-400 rounded absolute top-1"></div>
                </div>
              </div>
              <div class="text-center font-mono">
                <span class="knob-val text-xs text-slate-200 font-bold">-1.0 dB</span>
              </div>
            </div>
            <div class="text-[9px] text-slate-500 font-mono text-center">Dynamic Bell (Q: 0.7)</div>
          </div>

          <!-- Band 3 (Mid High) -->
          <div class="bg-slate-900/80 border border-slate-800 p-2.5 rounded-lg flex flex-col justify-between">
            <div class="flex items-center justify-between text-xs font-bold text-purple-400">
              <span>BAND 3 (MID HIGH)</span>
              <span class="text-[10px] font-mono text-slate-400">3.2 kHz</span>
            </div>
            <div class="flex items-center justify-around my-2">
              <div class="knob-container" data-param="eqMid2Gain" data-min="-12" data-max="12" data-value="2.0" data-unit="dB">
                <div class="knob-dial w-9 h-9 rounded-full bg-slate-800 border-2 border-purple-500 relative flex items-center justify-center shadow-md">
                  <div class="knob-pointer w-0.5 h-3 bg-purple-400 rounded absolute top-1"></div>
                </div>
              </div>
              <div class="text-center font-mono">
                <span class="knob-val text-xs text-slate-200 font-bold">+2.0 dB</span>
              </div>
            </div>
            <div class="text-[9px] text-slate-500 font-mono text-center">Dynamic Bell (Q: 1.2)</div>
          </div>

          <!-- Band 4 (High) -->
          <div class="bg-slate-900/80 border border-slate-800 p-2.5 rounded-lg flex flex-col justify-between">
            <div class="flex items-center justify-between text-xs font-bold text-emerald-400">
              <span>BAND 4 (HIGH)</span>
              <span class="text-[10px] font-mono text-slate-400">10 kHz</span>
            </div>
            <div class="flex items-center justify-around my-2">
              <div class="knob-container" data-param="eqHighGain" data-min="-12" data-max="12" data-value="1.8" data-unit="dB">
                <div class="knob-dial w-9 h-9 rounded-full bg-slate-800 border-2 border-emerald-500 relative flex items-center justify-center shadow-md">
                  <div class="knob-pointer w-0.5 h-3 bg-emerald-400 rounded absolute top-1"></div>
                </div>
              </div>
              <div class="text-center font-mono">
                <span class="knob-val text-xs text-slate-200 font-bold">+1.8 dB</span>
              </div>
            </div>
            <div class="text-[9px] text-slate-500 font-mono text-center">High Shelf</div>
          </div>
        </div>
      </div>

      <!-- VIEW 2: STEREO IMAGER VISUALIZER & CONTROLS (HIDDEN BY DEFAULT) -->
      <div id="viewImager" class="flex-1 flex flex-col p-4 gap-4 hidden">
        <div class="grid grid-cols-12 gap-4 flex-1">
          <!-- Polar Vectorscope Canvas (8 Cols) -->
          <div class="col-span-8 bg-slate-950 border border-slate-800/90 rounded-xl p-3 flex flex-col relative shadow-inner">
            <span class="text-xs font-bold text-cyan-400 uppercase font-mono mb-2">
              <i class="fa-solid fa-circle-nodes"></i> Polar Vectorscope & Correlation Meter
            </span>
            <div class="flex-1 relative w-full h-56 flex items-center justify-center">
              <canvas id="imagerCanvas" class="w-full h-full block"></canvas>
            </div>
          </div>

          <!-- Band Width Controls (4 Cols) -->
          <div class="col-span-4 oz-panel p-3 rounded-xl flex flex-col justify-between">
            <span class="text-xs font-bold text-slate-300 uppercase font-mono">4-Band Stereo Width</span>
            
            <div class="space-y-3 my-auto">
              <div>
                <div class="flex justify-between text-[11px] font-mono mb-1"><span class="text-cyan-400 font-bold">Band 1 (0-100Hz)</span><span id="w1Val">0%</span></div>
                <input id="w1Slider" type="range" min="-100" max="100" value="0" class="w-full accent-cyan-500 h-1 cursor-pointer">
              </div>
              <div>
                <div class="flex justify-between text-[11px] font-mono mb-1"><span class="text-amber-400 font-bold">Band 2 (100-1kHz)</span><span id="w2Val">+15%</span></div>
                <input id="w2Slider" type="range" min="-100" max="100" value="15" class="w-full accent-amber-500 h-1 cursor-pointer">
              </div>
              <div>
                <div class="flex justify-between text-[11px] font-mono mb-1"><span class="text-purple-400 font-bold">Band 3 (1k-6kHz)</span><span id="w3Val">+35%</span></div>
                <input id="w3Slider" type="range" min="-100" max="100" value="35" class="w-full accent-purple-500 h-1 cursor-pointer">
              </div>
              <div>
                <div class="flex justify-between text-[11px] font-mono mb-1"><span class="text-emerald-400 font-bold">Band 4 (6k-20kHz)</span><span id="w4Val">+50%</span></div>
                <input id="w4Slider" type="range" min="-100" max="100" value="50" class="w-full accent-emerald-500 h-1 cursor-pointer">
              </div>
            </div>
          </div>
        </div>
      </div>

      <!-- VIEW 3: MAXIMIZER / LIMITER VISUALIZER & CONTROLS (HIDDEN BY DEFAULT) -->
      <div id="viewMaximizer" class="flex-1 flex flex-col p-4 gap-4 hidden">
        <div class="oz-panel p-4 rounded-xl flex-1 grid grid-cols-12 gap-6 items-center">
          
          <!-- Left Gain & Output Level Panel -->
          <div class="col-span-4 flex flex-col items-center justify-center border-r border-slate-800 pr-4">
            <span class="text-xs font-bold text-slate-400 uppercase font-mono mb-3">Maximizer Gain Boost</span>
            
            <div class="knob-container my-2" data-param="maxGain" data-min="0" data-max="12" data-value="5.4" data-unit="dB">
              <div class="knob-dial w-24 h-24 rounded-full bg-slate-900 border-4 border-cyan-500 relative flex items-center justify-center shadow-2xl">
                <div class="knob-pointer w-1 h-8 bg-cyan-400 rounded absolute top-2"></div>
                <span class="knob-val text-sm font-bold font-mono text-cyan-300 z-10">+5.4 dB</span>
              </div>
            </div>

            <div class="w-full mt-4 bg-slate-950 p-2 rounded-lg border border-slate-800 text-xs font-mono flex justify-between items-center">
              <span class="text-slate-400">Ceiling Level:</span>
              <span class="text-cyan-400 font-bold">-0.10 dB</span>
            </div>
          </div>

          <!-- Center Upward Comp & Soft Clip Controls -->
          <div class="col-span-8 grid grid-cols-3 gap-4">
            <div class="bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center">
              <span class="text-[10px] font-bold text-slate-400 font-mono mb-2">UPWARD COMPRESS</span>
              <div class="knob-container" data-param="maxUpward" data-min="0" data-max="10" data-value="2.0" data-unit="dB">
                <div class="knob-dial w-12 h-12 rounded-full bg-slate-800 border-2 border-cyan-500 relative flex items-center justify-center">
                  <div class="knob-pointer w-0.5 h-4 bg-cyan-400 rounded absolute top-1"></div>
                </div>
              </div>
              <span class="knob-val text-xs font-mono text-slate-200 mt-2">+2.0 dB</span>
            </div>

            <div class="bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center">
              <span class="text-[10px] font-bold text-slate-400 font-mono mb-2">SOFT CLIPPER</span>
              <div class="knob-container" data-param="maxSoftClip" data-min="0" data-max="100" data-value="15" data-unit="%">
                <div class="knob-dial w-12 h-12 rounded-full bg-slate-800 border-2 border-amber-500 relative flex items-center justify-center">
                  <div class="knob-pointer w-0.5 h-4 bg-amber-400 rounded absolute top-1"></div>
                </div>
              </div>
              <span class="knob-val text-xs font-mono text-slate-200 mt-2">15%</span>
            </div>

            <div class="bg-slate-950 p-3 border border-slate-800 rounded-xl flex flex-col items-center">
              <span class="text-[10px] font-bold text-slate-400 font-mono mb-2">TRANSIENT EMPHASIS</span>
              <div class="knob-container" data-param="maxTransient" data-min="0" data-max="100" data-value="25" data-unit="%">
                <div class="knob-dial w-12 h-12 rounded-full bg-slate-800 border-2 border-emerald-500 relative flex items-center justify-center">
                  <div class="knob-pointer w-0.5 h-4 bg-emerald-400 rounded absolute top-1"></div>
                </div>
              </div>
              <span class="knob-val text-xs font-mono text-slate-200 mt-2">25%</span>
            </div>
          </div>

        </div>
      </div>

    </div>

    <!-- RIGHT SIDEBAR: MASTER INPUT / OUTPUT STEREO METER TOWER -->
    <div class="w-64 bg-slate-900 border-l border-slate-800/80 p-3 flex flex-col justify-between shadow-2xl font-mono text-xs z-20">
      <div class="flex items-center justify-between border-b border-slate-800 pb-2 mb-2">
        <span class="text-xs font-bold text-white tracking-wider uppercase flex items-center gap-1.5">
          <i class="fa-solid fa-sliders text-cyan-400"></i> I/O METERS
        </span>
        <span class="text-[9px] bg-cyan-950 border border-cyan-800 text-cyan-300 px-1 py-0.5 rounded">TRUE PEAK</span>
      </div>

      <!-- Peak / RMS Numeric Display -->
      <div class="grid grid-cols-2 gap-2 text-[10px] text-center mb-2">
        <div class="bg-slate-950 border border-slate-800/80 p-1.5 rounded">
          <div class="text-slate-500 font-bold">IN PEAK</div>
          <div id="inPeakText" class="text-cyan-400 font-bold font-mono">-14.2 dB</div>
        </div>
        <div class="bg-slate-950 border border-slate-800/80 p-1.5 rounded">
          <div class="text-slate-500 font-bold">OUT PEAK</div>
          <div id="outPeakText" class="text-emerald-400 font-bold font-mono">-0.1 dB</div>
        </div>
      </div>

      <!-- Level Meters Graphic Container -->
      <div class="flex-1 bg-slate-950 border border-slate-800/90 rounded-xl p-2 flex justify-around relative overflow-hidden my-1">
        <!-- Input Meters (L/R) -->
        <div class="flex flex-col items-center h-full">
          <span class="text-[9px] text-slate-500 mb-1">IN</span>
          <div class="flex-1 w-5 bg-slate-900 border border-slate-800 rounded relative overflow-hidden flex items-end">
            <canvas id="inMeterCanvas" class="w-full h-full block"></canvas>
          </div>
        </div>

        <!-- Output Meters (L/R) -->
        <div class="flex flex-col items-center h-full">
          <span class="text-[9px] text-slate-500 mb-1">OUT</span>
          <div class="flex-1 w-6 bg-slate-900 border border-slate-800 rounded relative overflow-hidden flex items-end">
            <canvas id="outMeterCanvas" class="w-full h-full block"></canvas>
          </div>
        </div>
      </div>

      <!-- Global Utility Buttons (Ozone Right Side Action Bar) -->
      <div class="grid grid-cols-2 gap-1.5 mt-2">
        <button id="masterBypassBtn" class="bg-slate-800 hover:bg-slate-700 text-slate-200 py-1.5 rounded text-[10px] font-bold border border-slate-700 transition-colors">
          Bypass
        </button>
        <button class="bg-slate-800 hover:bg-slate-700 text-slate-200 py-1.5 rounded text-[10px] font-bold border border-slate-700 transition-colors">
          Gain Match
        </button>
        <button class="bg-slate-800 hover:bg-slate-700 text-slate-300 py-1.5 rounded text-[10px] font-sans border border-slate-700">
          Codec
        </button>
        <button class="bg-slate-800 hover:bg-slate-700 text-slate-300 py-1.5 rounded text-[10px] font-sans border border-slate-700">
          Dither
        </button>
      </div>
    </div>

  </main>

  <!-- JAVASCRIPT: WEB AUDIO DSP GRAPH & OZONE STUDIO LOGIC -->
  <script>
    // State Store for Ozone Parameters
    const ozState = {
      isBypassed: false,
      activeModule: 'eq', // 'eq', 'imager', 'maximizer'
      
      // Module Actives
      eqActive: true,
      imagerActive: true,
      maximizerActive: true,

      // EQ Parameters
      eqLowGain: 1.5,
      eqMid1Gain: -1.0,
      eqMid2Gain: 2.0,
      eqHighGain: 1.8,

      // Imager Band Widths
      w1: 0,
      w2: 15,
      w3: 35,
      w4: 50,

      // Maximizer Parameters
      maxGain: 5.4,
      maxUpward: 2.0,
      maxSoftClip: 15,
      maxTransient: 25
    };

    // Web Audio Node Graph References
    let audioCtx = null;
    let sourceNode = null;
    let inputAnalyser = null;
    let eqLowFilter = null;
    let eqMid1Filter = null;
    let eqMid2Filter = null;
    let eqHighFilter = null;
    let maximizerBoostGain = null;
    let maximizerCompressor = null;
    let outputAnalyser = null;
    let isPlaying = false;

    // Web Audio Initialization
    function initOzoneAudioEngine() {
      if (audioCtx) return;

      const AudioCtxClass = window.AudioContext || window.webkitAudioContext;
      audioCtx = new AudioCtxClass();

      // Input Analyser
      inputAnalyser = audioCtx.createAnalyser();
      inputAnalyser.fftSize = 2048;

      // Dynamic EQ Filters
      eqLowFilter = audioCtx.createBiquadFilter();
      eqLowFilter.type = 'lowshelf';
      eqLowFilter.frequency.value = 100;

      eqMid1Filter = audioCtx.createBiquadFilter();
      eqMid1Filter.type = 'peaking';
      eqMid1Filter.frequency.value = 822;
      eqMid1Filter.Q.value = 0.7;

      eqMid2Filter = audioCtx.createBiquadFilter();
      eqMid2Filter.type = 'peaking';
      eqMid2Filter.frequency.value = 3200;
      eqMid2Filter.Q.value = 1.2;

      eqHighFilter = audioCtx.createBiquadFilter();
      eqHighFilter.type = 'highshelf';
      eqHighFilter.frequency.value = 10000;

      // Maximizer Boost Gain
      maximizerBoostGain = audioCtx.createGain();

      // Brickwall Limiter Compressor
      maximizerCompressor = audioCtx.createDynamicsCompressor();
      maximizerCompressor.threshold.value = -0.1;
      maximizerCompressor.knee.value = 0.0;
      maximizerCompressor.ratio.value = 20.0;
      maximizerCompressor.attack.value = 0.001;
      maximizerCompressor.release.value = 0.05;

      // Output Analyser
      outputAnalyser = audioCtx.createAnalyser();
      outputAnalyser.fftSize = 2048;

      // Connect Signal Chain Graph:
      // InputAnalyser -> EQ Low -> EQ Mid1 -> EQ Mid2 -> EQ High -> BoostGain -> Compressor -> OutputAnalyser -> Destination
      inputAnalyser.connect(eqLowFilter);
      eqLowFilter.connect(eqMid1Filter);
      eqMid1Filter.connect(eqMid2Filter);
      eqMid2Filter.connect(eqHighFilter);
      eqHighFilter.connect(maximizerBoostGain);
      maximizerBoostGain.connect(maximizerCompressor);
      maximizerCompressor.connect(outputAnalyser);
      outputAnalyser.connect(audioCtx.destination);

      updateAudioGraphValues();
    }

    function updateAudioGraphValues() {
      if (!audioCtx) return;
      const now = audioCtx.currentTime;

      eqLowFilter.gain.setTargetAtTime(ozState.eqActive ? ozState.eqLowGain : 0, now, 0.01);
      eqMid1Filter.gain.setTargetAtTime(ozState.eqActive ? ozState.eqMid1Gain : 0, now, 0.01);
      eqMid2Filter.gain.setTargetAtTime(ozState.eqActive ? ozState.eqMid2Gain : 0, now, 0.01);
      eqHighFilter.gain.setTargetAtTime(ozState.eqActive ? ozState.eqHighGain : 0, now, 0.01);

      const boostLinear = ozState.maximizerActive ? Math.pow(10, ozState.maxGain / 20) : 1.0;
      maximizerBoostGain.gain.setTargetAtTime(boostLinear, now, 0.01);
    }

    // Synthesize EDM / Pop Reference Audio Loop
    function startAudioDemo() {
      initOzoneAudioEngine();
      if (audioCtx.state === 'suspended') audioCtx.resume();
      stopAudioDemo();

      const sampleRate = audioCtx.sampleRate;
      const bufferSize = sampleRate * 4; // 4 second loop
      const buffer = audioCtx.createBuffer(2, bufferSize, sampleRate);
      const left = buffer.getChannelData(0);
      const right = buffer.getChannelData(1);

      for (let i = 0; i < bufferSize; i++) {
        const t = i / sampleRate;
        // Kick Drum
        const kickEnv = Math.max(0, 1 - (t % 0.5) * 8);
        const kick = Math.sin(2 * Math.PI * (55 * Math.exp(-(t % 0.5) * 18))) * kickEnv;

        // Snare / Claps
        const snareEnv = Math.max(0, 1 - ((t + 0.25) % 0.5) * 6);
        const snare = (Math.random() * 2 - 1) * snareEnv * 0.3;

        // EDM Chords (C min, Ab, Eb, Bb)
        const synth = (Math.sin(2 * Math.PI * 261.63 * t) + Math.sin(2 * Math.PI * 311.13 * t) + Math.sin(2 * Math.PI * 392.0 * t)) * 0.12;

        left[i] = kick * 0.6 + snare + synth;
        right[i] = kick * 0.6 + snare * 0.9 + synth * 0.95;
      }

      sourceNode = audioCtx.createBufferSource();
      sourceNode.buffer = buffer;
      sourceNode.loop = true;
      sourceNode.connect(inputAnalyser);
      sourceNode.start();

      isPlaying = true;
      document.getElementById('playAudioBtn').innerHTML = '<i class="fa-solid fa-pause text-[10px]"></i> <span>Pause Reference</span>';
    }

    function stopAudioDemo() {
      if (sourceNode) {
        try { sourceNode.stop(); } catch(e){}
        sourceNode.disconnect();
        sourceNode = null;
      }
      isPlaying = false;
      document.getElementById('playAudioBtn').innerHTML = '<i class="fa-solid fa-play text-[10px]"></i> <span>Play Reference Loop</span>';
    }

    // Canvas Render Loops
    function startCanvasRenderers() {
      const eqCanvas = document.getElementById('eqCanvas');
      const imagerCanvas = document.getElementById('imagerCanvas');
      const inMeterCanvas = document.getElementById('inMeterCanvas');
      const outMeterCanvas = document.getElementById('outMeterCanvas');

      const eqCtx = eqCanvas.getContext('2d');
      const imagerCtx = imagerCanvas.getContext('2d');
      const inCtx = inMeterCanvas.getContext('2d');
      const outCtx = outMeterCanvas.getContext('2d');

      function resizeAll() {
        eqCanvas.width = eqCanvas.clientWidth;
        eqCanvas.height = eqCanvas.clientHeight;
        imagerCanvas.width = imagerCanvas.clientWidth;
        imagerCanvas.height = imagerCanvas.clientHeight;
        inMeterCanvas.width = inMeterCanvas.clientWidth;
        inMeterCanvas.height = inMeterCanvas.clientHeight;
        outMeterCanvas.width = outMeterCanvas.clientWidth;
        outMeterCanvas.height = outMeterCanvas.clientHeight;
      }
      resizeAll();
      window.addEventListener('resize', resizeAll);

      const fftData = new Uint8Array(1024);

      function renderFrame() {
        requestAnimationFrame(renderFrame);

        // -------------------------------------------------------------
        // 1. RENDER DYNAMIC EQ & SPECTRUM
        // -------------------------------------------------------------
        const w = eqCanvas.width;
        const h = eqCanvas.height;
        eqCtx.clearRect(0, 0, w, h);

        // Draw Ozone-style Frequency Grid Lines
        eqCtx.strokeStyle = 'rgba(51, 65, 85, 0.3)';
        eqCtx.lineWidth = 1;
        eqCtx.font = '9px JetBrains Mono';
        eqCtx.fillStyle = '#475569';

        const freqs = [20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000];
        freqs.forEach(f => {
          const x = (Math.log10(f / 20) / Math.log10(20000 / 20)) * w;
          eqCtx.beginPath();
          eqCtx.moveTo(x, 0);
          eqCtx.lineTo(x, h);
          eqCtx.stroke();
          if (f >= 1000) eqCtx.fillText(`${f/1000}k`, x + 3, h - 6);
          else eqCtx.fillText(`${f}`, x + 3, h - 6);
        });

        // Draw FFT Spectrum Fill
        if (outputAnalyser && isPlaying) {
          outputAnalyser.getByteFrequencyData(fftData);
          eqCtx.fillStyle = 'rgba(56, 189, 248, 0.15)';
          const barWidth = w / 128;
          for (let i = 0; i < 128; i++) {
            const val = fftData[i * 4] / 255;
            const barHeight = val * h;
            eqCtx.fillRect(i * barWidth, h - barHeight, barWidth - 1, barHeight);
          }
        }

        // Draw Composite EQ Response Curve
        eqCtx.strokeStyle = '#38bdf8';
        eqCtx.lineWidth = 2.5;
        eqCtx.beginPath();

        for (let x = 0; x < w; x++) {
          const freq = 20 * Math.pow(20000 / 20, x / w);
          let gainDb = 0;

          if (ozState.eqActive) {
            // Low Shelf
            gainDb += ozState.eqLowGain / (1 + Math.pow(freq / 100, 2));
            // Mid 1 Bell
            gainDb += ozState.eqMid1Gain * Math.exp(-Math.pow(Math.log(freq / 822), 2) * 2);
            // Mid 2 Bell
            gainDb += ozState.eqMid2Gain * Math.exp(-Math.pow(Math.log(freq / 3200), 2) * 2);
            // High Shelf
            gainDb += ozState.eqHighGain / (1 + Math.pow(10000 / freq, 2));
          }

          const y = h / 2 - (gainDb / 18) * (h / 2);
          if (x === 0) eqCtx.moveTo(x, y);
          else eqCtx.lineTo(x, y);
        }
        eqCtx.stroke();

        // -------------------------------------------------------------
        // 2. RENDER IMAGER POLAR VECTORSCOPE
        // -------------------------------------------------------------
        const iw = imagerCanvas.width;
        const ih = imagerCanvas.height;
        imagerCtx.clearRect(0, 0, iw, ih);

        // Scope Polar Circle Guides
        imagerCtx.strokeStyle = 'rgba(51, 65, 85, 0.4)';
        imagerCtx.lineWidth = 1;
        imagerCtx.beginPath();
        imagerCtx.arc(iw / 2, ih / 2, ih / 3, 0, Math.PI * 2);
        imagerCtx.stroke();

        // Scope Dynamic Stereo Dots Animation
        if (isPlaying) {
          imagerCtx.fillStyle = '#38bdf8';
          for (let i = 0; i < 40; i++) {
            const angle = (Math.random() - 0.5) * (Math.PI / 2) + (-Math.PI / 2);
            const radius = (Math.random() * (ih / 3.2));
            const x = iw / 2 + Math.cos(angle) * radius * (1 + ozState.w3 / 100);
            const y = ih / 2 + Math.sin(angle) * radius;
            imagerCtx.fillRect(x, y, 2, 2);
          }
        }

        // -------------------------------------------------------------
        // 3. RENDER I/O STEREO METER TOWERS
        // -------------------------------------------------------------
        renderMeterBar(inCtx, inMeterCanvas.width, inMeterCanvas.height, isPlaying ? 0.55 : 0.05, 'inPeakText');
        renderMeterBar(outCtx, outMeterCanvas.width, outMeterCanvas.height, isPlaying ? 0.85 : 0.05, 'outPeakText');
      }

      function renderMeterBar(ctx, w, h, level, textId) {
        ctx.clearRect(0, 0, w, h);
        const barH = level * h;

        const grad = ctx.createLinearGradient(0, h, 0, 0);
        grad.addColorStop(0, '#38bdf8');
        grad.addColorStop(0.7, '#f59e0b');
        grad.addColorStop(1, '#ef4444');

        ctx.fillStyle = grad;
        ctx.fillRect(2, h - barH, w - 4, barH);

        const dbVal = (20 * Math.log10(level)).toFixed(1);
        document.getElementById(textId).innerText = isPlaying ? `${dbVal} dB` : '-inf dB';
      }

      renderFrame();
    }

    // Rotary Knob Drag Interactions
    function setupKnobs() {
      document.querySelectorAll('.knob-container').forEach(knob => {
        let isDragging = false;
        let startY = 0;
        let startVal = parseFloat(knob.dataset.value);

        const param = knob.dataset.param;
        const min = parseFloat(knob.dataset.min);
        const max = parseFloat(knob.dataset.max);
        const unit = knob.dataset.unit || '';

        const dial = knob.querySelector('.knob-dial');
        const valText = knob.parentElement.querySelector('.knob-val');

        function updateUI(val) {
          const pct = (val - min) / (max - min);
          const angle = -135 + pct * 270;
          dial.style.transform = `rotate(${angle}deg)`;

          if (valText) {
            valText.innerText = `${val > 0 && unit==='dB' ? '+' : ''}${val.toFixed(1)} ${unit}`;
          }

          ozState[param] = val;
          updateAudioGraphValues();
        }

        updateUI(startVal);

        knob.addEventListener('pointerdown', e => {
          isDragging = true;
          startY = e.clientY;
          startVal = ozState[param];
          knob.setPointerCapture(e.pointerId);
        });

        knob.addEventListener('pointermove', e => {
          if (!isDragging) return;
          const deltaY = startY - e.clientY;
          const range = max - min;
          let newVal = startVal + (deltaY / 100) * range;
          newVal = Math.min(max, Math.max(min, newVal));
          updateUI(newVal);
        });

        knob.addEventListener('pointerup', e => {
          isDragging = false;
          knob.releasePointerCapture(e.pointerId);
        });
      });
    }

    // Module Switcher Navigation Logic
    function setupModuleNavigation() {
      const cardEq = document.getElementById('cardDynamicEq');
      const cardImager = document.getElementById('cardImager');
      const cardMaximizer = document.getElementById('cardMaximizer');

      const viewEq = document.getElementById('viewDynamicEq');
      const viewImager = document.getElementById('viewImager');
      const viewMaximizer = document.getElementById('viewMaximizer');

      function switchModule(activeCard, activeView, name) {
        [cardEq, cardImager, cardMaximizer].forEach(c => c.classList.remove('oz-card-active'));
        [viewEq, viewImager, viewMaximizer].forEach(v => v.classList.add('hidden'));

        activeCard.classList.add('oz-card-active');
        activeView.classList.remove('hidden');
        ozState.activeModule = name;
      }

      cardEq.addEventListener('click', () => switchModule(cardEq, viewEq, 'eq'));
      cardImager.addEventListener('click', () => switchModule(cardImager, viewImager, 'imager'));
      cardMaximizer.addEventListener('click', () => switchModule(cardMaximizer, viewMaximizer, 'maximizer'));
    }

    // DOM Load Init
    window.addEventListener('load', () => {
      document.getElementById('playAudioBtn').addEventListener('click', () => {
        if (isPlaying) stopAudioDemo();
        else startAudioDemo();
      });

      document.getElementById('stopAudioBtn').addEventListener('click', stopAudioDemo);

      setupKnobs();
      setupModuleNavigation();
      startCanvasRenderers();
    });
  </script>
</body>
</html>

```