[{"data":1,"prerenderedAt":399},["ShallowReactive",2],{"navigation_docs":3,"-guide-websocket-protocol":81,"-guide-websocket-protocol-surround":394},[4,41,62],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":40},"Guide","i-lucide-book-open","\u002Fguide","1.guide",[10,15,20,25,30,35],{"title":11,"path":12,"stem":13,"icon":14},"Introduction","\u002Fguide\u002Fintroduction","1.guide\u002F1.introduction","i-lucide-house",{"title":16,"path":17,"stem":18,"icon":19},"How it works","\u002Fguide\u002Fhow-it-works","1.guide\u002F2.how-it-works","i-lucide-workflow",{"title":21,"path":22,"stem":23,"icon":24},"Data modes","\u002Fguide\u002Fdata-modes","1.guide\u002F3.data-modes","i-lucide-git-fork",{"title":26,"path":27,"stem":28,"icon":29},"Options","\u002Fguide\u002Foptions","1.guide\u002F4.options","i-lucide-settings-2",{"title":31,"path":32,"stem":33,"icon":34},"Gradients","\u002Fguide\u002Fgradients","1.guide\u002F5.gradients","i-lucide-palette",{"title":36,"path":37,"stem":38,"icon":39},"WebSocket protocol","\u002Fguide\u002Fwebsocket-protocol","1.guide\u002F6.websocket-protocol","i-lucide-radio",false,{"title":42,"icon":43,"path":44,"stem":45,"children":46,"page":40},"Core","i-lucide-box","\u002Fcore","2.core",[47,52,57],{"title":48,"path":49,"stem":50,"icon":51},"Installation","\u002Fcore\u002Finstallation","2.core\u002F1.installation","i-lucide-download",{"title":53,"path":54,"stem":55,"icon":56},"API reference","\u002Fcore\u002Fapi","2.core\u002F2.api","i-lucide-code",{"title":58,"path":59,"stem":60,"icon":61},"Audio engines","\u002Fcore\u002Faudio-engines","2.core\u002F3.audio-engines","i-lucide-audio-lines",{"title":63,"icon":64,"path":65,"stem":66,"children":67,"page":40},"Vue","i-simple-icons-vuedotjs","\u002Fvue","3.vue",[68,71,76],{"title":48,"path":69,"stem":70,"icon":51},"\u002Fvue\u002Finstallation","3.vue\u002F1.installation",{"title":72,"path":73,"stem":74,"icon":75},"Props, events & methods","\u002Fvue\u002Fprops","3.vue\u002F2.props","i-lucide-sliders-horizontal",{"title":77,"path":78,"stem":79,"icon":80},"Composables","\u002Fvue\u002Fcomposables","3.vue\u002F3.composables","i-lucide-plug",{"id":82,"title":36,"body":83,"description":387,"extension":388,"links":389,"meta":390,"navigation":391,"path":37,"seo":392,"stem":38,"__hash__":393},"docs\u002F1.guide\u002F6.websocket-protocol.md",{"type":84,"value":85,"toc":381},"minimark",[86,100,105,115,120,210,215,235,238,242,261,265,271,351,377],[87,88,89,90,94,95,99],"p",{},"In ",[91,92,93],"code",{},"websocket"," mode the visualizer connects to a server that has ",[96,97,98],"strong",{},"already computed"," the\nFFT and streams it as compact binary frames — so the browser only draws. This lets the FFT\nrun on a separate device (a Raspberry Pi, a media server) feeding any number of browsers.",[101,102,104],"h2",{"id":103},"the-pre-computed-fft-protocol","The pre-computed-FFT protocol",[87,106,107,110,111,114],{},[91,108,109],{},"mode: 'websocket'"," expects a server that sends ",[96,112,113],{},"pre-computed"," FFT frames.",[87,116,117],{},[96,118,119],{},"1. Config message (JSON), once on connect:",[121,122,127],"pre",{"className":123,"code":124,"language":125,"meta":126,"style":126},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{ \"type\": \"config\", \"mode\": \"fft\", \"bins\": 80, \"fps\": 120 }\n","json","",[91,128,129],{"__ignoreMap":126},[130,131,134,138,141,145,148,151,153,157,159,162,164,167,169,171,173,176,178,180,182,185,187,189,193,195,197,200,202,204,207],"span",{"class":132,"line":133},"line",1,[130,135,137],{"class":136},"sMK4o","{",[130,139,140],{"class":136}," \"",[130,142,144],{"class":143},"spNyl","type",[130,146,147],{"class":136},"\"",[130,149,150],{"class":136},":",[130,152,140],{"class":136},[130,154,156],{"class":155},"sfazB","config",[130,158,147],{"class":136},[130,160,161],{"class":136},",",[130,163,140],{"class":136},[130,165,166],{"class":143},"mode",[130,168,147],{"class":136},[130,170,150],{"class":136},[130,172,140],{"class":136},[130,174,175],{"class":155},"fft",[130,177,147],{"class":136},[130,179,161],{"class":136},[130,181,140],{"class":136},[130,183,184],{"class":143},"bins",[130,186,147],{"class":136},[130,188,150],{"class":136},[130,190,192],{"class":191},"sbssI"," 80",[130,194,161],{"class":136},[130,196,140],{"class":136},[130,198,199],{"class":143},"fps",[130,201,147],{"class":136},[130,203,150],{"class":136},[130,205,206],{"class":191}," 120",[130,208,209],{"class":136}," }\n",[87,211,212],{},[96,213,214],{},"2. Binary FFT frames, continuously:",[216,217,218,229,232],"ul",{},[219,220,221,222,225,226,228],"li",{},"One ",[91,223,224],{},"uint8"," (0–255) per frequency bin — ",[91,227,184],{}," bytes per frame",[219,230,231],{},"0 = silence, 255 = maximum amplitude",[219,233,234],{},"Typically 100 Hz – 18 kHz, exponentially spaced",[87,236,237],{},"For best results the server should capture at 48 kHz+, apply a Hann\u002FHamming window, compute\na 1024–2048-point FFT, map to exponentially-spaced bands, apply A-weighting, convert to dB,\nnormalize to 0–255, and stream at 60–120 fps.",[101,239,241],{"id":240},"reference-servers","Reference servers",[87,243,244,245,248,249,252,253,256,257,260],{},"The repository's ",[91,246,247],{},"backend-examples\u002F"," has servers that capture system audio, compute FFT, and\nstream it in this format: ",[96,250,251],{},"Python"," (pyalsaaudio + numpy, incl. a Raspberry Pi variant),\n",[96,254,255],{},"Node.js"," (node-audiorecorder + fft.js), and ",[96,258,259],{},"Rust"," (cpal + rustfft).",[101,262,264],{"id":263},"two-different-websocket-protocols","Two different WebSocket protocols",[87,266,267,268,270],{},"Don't confuse the visualizer's ",[91,269,93],{}," mode with the raw-PCM engines:",[272,273,274,292],"table",{},[275,276,277],"thead",{},[278,279,280,283,286,289],"tr",{},[281,282],"th",{},[281,284,285],{},"Sends over the wire",[281,287,288],{},"FFT runs",[281,290,291],{},"Consumed by",[293,294,295,323],"tbody",{},[278,296,297,308,313,316],{},[298,299,300,303,304],"td",{},[96,301,302],{},"Pre-computed FFT"," ",[305,306,307],"em",{},"(this page)",[298,309,310,312],{},[91,311,224],{}," FFT magnitudes",[298,314,315],{},"On the server",[298,317,318,303,321],{},[91,319,320],{},"FFTVisualizer",[91,322,109],{},[278,324,325,330,333,336],{},[298,326,327],{},[96,328,329],{},"Raw PCM",[298,331,332],{},"Interleaved PCM audio",[298,334,335],{},"In the browser (WASM)",[298,337,338,339,345,346],{},"Core ",[340,341,342],"a",{"href":59},[91,343,344],{},"createWebSocketFft"," \u002F Vue ",[340,347,348],{"href":78},[91,349,350],{},"useWebSocketFft",[87,352,353,354,357,358,361,362,365,366,369,370,373,374,376],{},"The raw-PCM path streams audio and does the FFT client-side; feed its output into the\nvisualizer with ",[91,355,356],{},"mode: 'external'",". Its config message uses ",[91,359,360],{},"\"mode\": \"pcm\""," with\n",[91,363,364],{},"sampleRate",", ",[91,367,368],{},"bitDepth",", and ",[91,371,372],{},"channels"," fields instead of the ",[91,375,175],{}," config above.",[378,379,380],"style",{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":126,"searchDepth":382,"depth":382,"links":383},2,[384,385,386],{"id":103,"depth":382,"text":104},{"id":240,"depth":382,"text":241},{"id":263,"depth":382,"text":264},"The remote-FFT wire protocol the visualizer speaks in websocket mode, and the reference servers.","md",null,{},{"icon":39},{"title":36,"description":387},"7pCpddfbPtGW6jjruYSCbxCzWRlS4qrpHNQ358FD3Ko",[395,397],{"title":31,"path":32,"stem":33,"description":396,"icon":34,"children":-1},"Color the bars with a built-in preset or your own CSS-color stops.",{"title":48,"path":49,"stem":50,"description":398,"icon":51,"children":-1},"Install fft-visualizer-core and render a spectrum onto a canvas — no framework required.",1784963432762]