{"spec_id":"waveform-audio","library":"echarts","language":"javascript","code":"// anyplot.ai\n// waveform-audio: Audio Waveform Plot\n// Library: echarts 5.5.1 | JavaScript 22.22.3\n// Quality: 92/100 | Created: 2026-06-03\n//# anyplot-orientation: landscape\n\nconst t = window.ANYPLOT_TOKENS;\n\n// Inline RGB components for gradient stops (palette[0] = #009E73)\nconst waveColor = t.palette[0];\nconst wR = parseInt(waveColor.slice(1, 3), 16);\nconst wG = parseInt(waveColor.slice(3, 5), 16);\nconst wB = parseInt(waveColor.slice(5, 7), 16);\n\n// Synthetic speech-like waveform: 5000 samples over 1 second\nconst N = 5000;\nconst duration = 1.0;\nconst rawData = [];\n\nfor (let i = 0; i < N; i++) {\n    const time = (i / N) * duration;\n    // Three Gaussian syllabic envelopes at 0.14 s, 0.47 s, 0.81 s\n    const env1 = Math.exp(-32 * Math.pow(time - 0.14, 2));\n    const env2 = Math.exp(-28 * Math.pow(time - 0.47, 2));\n    const env3 = Math.exp(-38 * Math.pow(time - 0.81, 2));\n    const envelope = (env1 + 0.88 * env2 + 0.72 * env3) * 0.85;\n    // 175 Hz fundamental with four harmonics\n    const f0 = 175;\n    const carrier =\n        0.48 * Math.sin(2 * Math.PI * f0 * time) +\n        0.26 * Math.sin(2 * Math.PI * 2 * f0 * time) +\n        0.14 * Math.sin(2 * Math.PI * 3 * f0 * time) +\n        0.09 * Math.sin(2 * Math.PI * 4 * f0 * time) +\n        0.03 * Math.sin(2 * Math.PI * 5 * f0 * time);\n    rawData.push(Math.max(-1, Math.min(1, carrier * envelope)));\n}\n\n// Downsample to 300-bin min/max envelope to avoid aliasing\nconst BINS = 300;\nconst binSize = Math.floor(N / BINS);\nconst maxEnv = [], minEnv = [];\nfor (let b = 0; b < BINS; b++) {\n    const start = b * binSize;\n    const end = Math.min(start + binSize, N);\n    const tCenter = ((start + end) / 2 / N) * duration;\n    let bMax = -Infinity, bMin = Infinity;\n    for (let k = start; k < end; k++) {\n        if (rawData[k] > bMax) bMax = rawData[k];\n        if (rawData[k] < bMin) bMin = rawData[k];\n    }\n    maxEnv.push([tCenter, bMax]);\n    minEnv.push([tCenter, bMin]);\n}\n\nconst chart = echarts.init(document.getElementById(\"container\"));\n\nchart.setOption({\n    animation: false,\n    backgroundColor: \"transparent\",\n    title: {\n        text: \"waveform-audio · javascript · echarts · anyplot.ai\",\n        left: \"center\",\n        top: 18,\n        textStyle: { color: t.ink, fontSize: 22, fontWeight: \"bold\" }\n    },\n    grid: { left: 95, right: 50, top: 80, bottom: 85, containLabel: true },\n    xAxis: {\n        type: \"value\",\n        name: \"Time (s)\",\n        nameLocation: \"middle\",\n        nameGap: 46,\n        nameTextStyle: { color: t.inkSoft, fontSize: 18 },\n        axisLabel: {\n            color: t.inkSoft,\n            fontSize: 14,\n            formatter: (v) => (v === 0 ? \"0\" : v.toFixed(2))\n        },\n        axisLine: { show: true, lineStyle: { color: t.inkSoft } },\n        axisTick: { lineStyle: { color: t.inkSoft } },\n        splitLine: { show: false },\n        min: 0,\n        max: 1.0\n    },\n    yAxis: {\n        type: \"value\",\n        name: \"Amplitude\",\n        nameLocation: \"middle\",\n        nameGap: 52,\n        nameTextStyle: { color: t.inkSoft, fontSize: 18 },\n        axisLabel: {\n            color: t.inkSoft,\n            fontSize: 14,\n            formatter: (v) => (v === 0 ? \"0\" : v.toFixed(1))\n        },\n        axisLine: { show: true, lineStyle: { color: t.inkSoft } },\n        axisTick: { lineStyle: { color: t.inkSoft } },\n        splitLine: { lineStyle: { color: t.grid } },\n        min: -1.0,\n        max: 1.0\n    },\n    series: [\n        {\n            // Upper envelope: max amplitude per bin\n            type: \"line\",\n            data: maxEnv,\n            symbol: \"none\",\n            smooth: false,\n            lineStyle: { color: waveColor, width: 0.8, opacity: 0.9 },\n            areaStyle: {\n                origin: 0,\n                color: {\n                    type: \"linear\",\n                    x: 0, y: 0, x2: 0, y2: 1,\n                    colorStops: [\n                        { offset: 0, color: `rgba(${wR},${wG},${wB},0.62)` },\n                        { offset: 1, color: `rgba(${wR},${wG},${wB},0.05)` }\n                    ]\n                }\n            },\n            markLine: {\n                silent: true,\n                data: [{ yAxis: 0 }],\n                lineStyle: { color: t.inkSoft, width: 1.5, type: \"solid\", opacity: 0.5 },\n                label: { show: false },\n                symbol: [\"none\", \"none\"]\n            },\n            markArea: {\n                silent: true,\n                label: {\n                    show: true,\n                    position: \"insideTop\",\n                    color: t.inkSoft,\n                    fontSize: 12\n                },\n                itemStyle: {\n                    color: `rgba(${wR},${wG},${wB},0.07)`,\n                    borderColor: `rgba(${wR},${wG},${wB},0.22)`,\n                    borderWidth: 1\n                },\n                data: [\n                    [{ name: \"Syllable 1\", xAxis: 0.05 }, { xAxis: 0.24 }],\n                    [{ name: \"Syllable 2\", xAxis: 0.36 }, { xAxis: 0.58 }],\n                    [{ name: \"Syllable 3\", xAxis: 0.70 }, { xAxis: 0.92 }]\n                ]\n            }\n        },\n        {\n            // Lower envelope: min amplitude per bin\n            type: \"line\",\n            data: minEnv,\n            symbol: \"none\",\n            smooth: false,\n            lineStyle: { color: waveColor, width: 0.8, opacity: 0.9 },\n            areaStyle: {\n                origin: 0,\n                color: {\n                    type: \"linear\",\n                    x: 0, y: 0, x2: 0, y2: 1,\n                    colorStops: [\n                        { offset: 0, color: `rgba(${wR},${wG},${wB},0.05)` },\n                        { offset: 1, color: `rgba(${wR},${wG},${wB},0.62)` }\n                    ]\n                }\n            }\n        }\n    ]\n});\n"}