---
name: "dynamic-audio-element"
description: "Demonstrates HTMLMediaElement audio playback by generating a WAV file from raw PCM math and playing it via an `<audio>` element. Tests the HTMLMediaElement.play() audio capture path."
metadata:
  version: "1.0.0"
  runtime:
    entrypoint: "index.html"
    timeout_seconds: 10
disable-model-invocation: true
---

# Dynamic Audio Element

Generates WAV audio from raw PCM data and plays it via HTMLMediaElement.

## Core Concepts
- PCM audio sample generation (sine wave math)
- WAV file format construction (RIFF header)
- `Blob` + `URL.createObjectURL` for in-memory audio
- `HTMLAudioElement.play()` for playback

## Usage
Automatic playback of a 4-note arpeggio (A4, C5, E5, G5). Each note is a freshly generated 400ms WAV.

---

## Files

### `index.html`

```html
<!DOCTYPE html>
<html lang="en"><head>
<meta charset="UTF-8"><title>Audio Buffer Synth</title>
<style>
  *{box-sizing:border-box;margin:0;padding:0}
  body{background:#0a0a0f;color:#e5e7eb;font-family:monospace;display:flex;flex-direction:column;align-items:center;justify-content:center;height:100vh;gap:20px}
  h1{color:#06b6d4;font-size:1.4rem}
  .subtitle{color:#6b7280;font-size:.8rem}
  canvas{border:1px solid #374151;border-radius:8px}
  .status{font-size:.85rem;color:#9ca3af}
  .note{font-size:2rem;color:#67e8f9;font-weight:bold}
</style>
</head><body>
<h1>&#9836; Audio Buffer Synth</h1>
<p class="subtitle">PCM generation &#8594; AudioBuffer &#8594; Web Audio playback</p>
<div class="note" id="note">&#8212;</div>
<canvas id="scope" width="480" height="80"></canvas>
<div class="status" id="status">Initializing&#8230;</div>
<script>
(function(){
  const AudioCtx = window.AudioContext || window.webkitAudioContext;
  if(!AudioCtx){ document.getElementById('status').textContent='AudioContext not supported'; return; }
  const ctx = new AudioCtx();
  const analyser = ctx.createAnalyser();
  analyser.fftSize = 512;
  analyser.connect(ctx.destination);
  const canvas = document.getElementById('scope');
  const g = canvas.getContext('2d');
  function drawScope(){
    requestAnimationFrame(drawScope);
    const buf = new Uint8Array(analyser.frequencyBinCount);
    analyser.getByteTimeDomainData(buf);
    g.fillStyle='#0a0a0f'; g.fillRect(0,0,480,80);
    g.strokeStyle='#06b6d4'; g.lineWidth=2; g.beginPath();
    const sliceW=480/buf.length;
    for(let i=0;i<buf.length;i++){
      const y=(buf[i]/128)*40;
      i===0?g.moveTo(i*sliceW,y):g.lineTo(i*sliceW,y);
    }
    g.stroke();
  }
  drawScope();
  function makePcmBuffer(freq, duration){
    const sr = ctx.sampleRate;
    const n = Math.floor(sr * duration);
    const buf = ctx.createBuffer(1, n, sr);
    const data = buf.getChannelData(0);
    for(let i=0;i<n;i++){
      const t=i/sr;
      const env=t<0.03?t/0.03:t>duration-0.08?(duration-t)/0.08:1;
      data[i]=Math.sin(2*Math.PI*freq*t)*env*0.3;
    }
    return buf;
  }
  function playNote(freq, name){
    return new Promise(function(resolve){
      const buf = makePcmBuffer(freq, 0.45);
      const src = ctx.createBufferSource();
      src.buffer = buf;
      src.connect(analyser);
      src.onended = resolve;
      src.start();
      document.getElementById('note').textContent = name;
      document.getElementById('status').textContent = 'Playing '+name+' ('+freq.toFixed(2)+' Hz)';
    });
  }
  async function run(){
    await new Promise(function(r){ setTimeout(r,100); });
    const notes=[['A4',440],['C5',523.25],['E5',659.26],['G5',783.99]];
    for(const entry of notes){
      await playNote(entry[1], entry[0]);
      await new Promise(function(r){ setTimeout(r,100); });
    }
    document.getElementById('status').textContent='PCM arpeggio complete';
    document.getElementById('note').textContent='done';
  }
  run();
})();
</script>
</body></html>
```
