36e8ceaInteractive explorer for compressibility of quantized filtered Gaussian time seriesJeremy Magland 1import { useEffect, useMemo, useRef, useState } from 'react'
2import Controls from './components/Controls'
3import FilterViz from './components/FilterViz'
4import ScrollingView from './components/ScrollingView'
5import CompressionChart from './components/CompressionChart'
6import MathSection from './components/MathSection'
7import { DEFAULT_SPEC, designKernel, kernelNorm } from './model/filters'
5bab85aRatio-first chart, quantization-floor theory formula, line-segment view, fixed latent dataJeremy Magland 8import { theoreticalRateBits } from './model/theory'
36e8ceaInteractive explorer for compressibility of quantized filtered Gaussian time seriesJeremy Magland 9import type { CodecResult } from './compress/codecs'
10import type { CompressRequest, CompressResponse } from './worker/compressWorker'
12const BLOCK_SIZE = 120000
13const BLOCK_SEED = 20260729
15interface CompressionState {
16 results: CodecResult[]
17 empiricalStd: number
18 computing: boolean
19 error: string | null
20}
22/** The nine codec sizes, measured in a worker on a debounced parameter set. */
23function useCompression(kernel: Float64Array, sigma: number, dither: boolean): CompressionState {
24 const [state, setState] = useState<CompressionState>({
25 results: [],
26 empiricalStd: 0,
27 computing: true,
28 error: null,
29 })
30 const workerRef = useRef<Worker | null>(null)
31 const idRef = useRef(0)
33 useEffect(() => {
34 const worker = new Worker(new URL('./worker/compressWorker.ts', import.meta.url), {
35 type: 'module',
36 })
37 worker.onmessage = (e: MessageEvent<CompressResponse>) => {
38 if (e.data.id !== idRef.current) return
39 setState({
40 results: e.data.error ? [] : e.data.results,
41 empiricalStd: e.data.empiricalStd,
42 computing: false,
43 error: e.data.error ?? null,
44 })
45 }
46 workerRef.current = worker
47 return () => {
48 worker.terminate()
49 workerRef.current = null
50 }
51 }, [])
53 useEffect(() => {
54 setState(s => ({ ...s, computing: true }))
55 const id = ++idRef.current
56 const timer = setTimeout(() => {
57 const request: CompressRequest = {
58 id,
59 kernel,
60 sigma,
61 dither,
62 blockSize: BLOCK_SIZE,
63 seed: BLOCK_SEED,
64 }
65 workerRef.current?.postMessage(request)
66 }, 250)
67 return () => clearTimeout(timer)
68 }, [kernel, sigma, dither])
70 return state
71}
73export default function App() {
74 const [sigma, setSigma] = useState(5)
75 const [sampleRateHz, setSampleRateHz] = useState(30000)
76 const [spec, setSpec] = useState(DEFAULT_SPEC)
77 const [dither, setDither] = useState(false)
79 const kernel = useMemo(() => designKernel(spec, sampleRateHz), [spec, sampleRateHz])
80 const sigmaY = useMemo(() => {
81 const filtered = sigma * kernelNorm(kernel)
82 return dither ? Math.sqrt(filtered * filtered + 1 / 12) : filtered
83 }, [kernel, sigma, dither])
5bab85aRatio-first chart, quantization-floor theory formula, line-segment view, fixed latent dataJeremy Magland 84 const theoryBits = useMemo(() => theoreticalRateBits(kernel, sigma, dither), [kernel, sigma, dither])
36e8ceaInteractive explorer for compressibility of quantized filtered Gaussian time seriesJeremy Magland 85 const compression = useCompression(kernel, sigma, dither)
87 return (
88 <div className="app">
89 <header className="app-header">
90 <h1>Time-series compressibility</h1>
91 <p>
92 Gaussian noise → FIR filter → optional dither → round to integers. How well can the
93 integer stream be losslessly compressed, and does the spectral entropy-rate formula
94 predict the limit?
95 </p>
96 </header>
98 <section className="card">
99 <h2>Model</h2>
100 <Controls
101 sigma={sigma}
102 setSigma={setSigma}
103 sampleRateHz={sampleRateHz}
104 setSampleRateHz={setSampleRateHz}
105 spec={spec}
106 setSpec={setSpec}
107 dither={dither}
108 setDither={setDither}
109 />
110 </section>
112 <section className="card">
113 <h2>Filter</h2>
114 <FilterViz kernel={kernel} sampleRateHz={sampleRateHz} sigma={sigma} />
115 </section>
117 <section className="card">
118 <h2>Quantized signal z</h2>
119 <ScrollingView kernel={kernel} sigma={sigma} dither={dither} sigmaY={sigmaY} />
120 <p className="card-note">
5bab85aRatio-first chart, quantization-floor theory formula, line-segment view, fixed latent dataJeremy Magland 121 A window of samples from the model, drawn from a fixed latent noise sequence — changing
122 σ, the filter, or dither transforms the same underlying data, so the trace morphs
123 rather than resampling. Press play to advance through the sequence.
36e8ceaInteractive explorer for compressibility of quantized filtered Gaussian time seriesJeremy Magland 124 </p>
125 </section>
127 <section className="card">
128 <h2>Compression</h2>
129 <div className="stat-row">
130 <div className="stat">
131 <span className="label">predicted std of z</span>
132 <span className="value">
133 {sigmaY.toFixed(2)} <small>steps</small>
134 </span>
135 </div>
136 <div className="stat">
137 <span className="label">measured std of z</span>
138 <span className="value">
139 {compression.results.length > 0 ? compression.empiricalStd.toFixed(2) : '…'}{' '}
140 <small>steps</small>
141 </span>
142 </div>
143 <div className="stat">
5bab85aRatio-first chart, quantization-floor theory formula, line-segment view, fixed latent dataJeremy Magland 144 <span className="label">theoretical rate R</span>
36e8ceaInteractive explorer for compressibility of quantized filtered Gaussian time seriesJeremy Magland 145 <span className="value">
146 {theoryBits.toFixed(2)} <small>bits/sample</small>
147 </span>
148 </div>
149 <div className="stat">
150 <span className="label">implied best ratio</span>
151 <span className="value">{theoryBits > 0 ? `${(16 / theoryBits).toFixed(2)}×` : '—'}</span>
152 </div>
153 </div>
154 {compression.error ? (
155 <p className="card-note">Compression failed: {compression.error}</p>
156 ) : (
157 <CompressionChart
158 results={compression.results}
159 theoryBits={theoryBits}
160 computing={compression.computing}
161 />
162 )}
163 <p className="card-note">
5bab85aRatio-first chart, quantization-floor theory formula, line-segment view, fixed latent dataJeremy Magland 164 Measured on a {BLOCK_SIZE.toLocaleString()}-sample block of the same latent data the
165 signal view shows; sizes include everything a decoder needs (ANS symbol table, LPC
166 coefficients). Baseline is raw int16 (16 bits/sample). The dashed line is the
167 theoretical rate R from the spectral formula in the math section — approximate where
168 quantization dominates the spectrum (see the S(f) = 1 threshold on the response plot).
36e8ceaInteractive explorer for compressibility of quantized filtered Gaussian time seriesJeremy Magland 169 </p>
170 </section>
172 <section className="card">
173 <h2>The math</h2>
174 <MathSection />
175 </section>
176 </div>
177 )
178}