/ concept-collection / timeseries-compressibility
Sign in
concept-collection / timeseries-compressibility
Pin the parameter bar, put compression first, shrink signal and filter panels
Jeremy Magland <jmagland@flatironinstitute.org> committed commit 8a09dfcb0df8 parent 5bab85a Browse files
4 changed files+66−29
src/App.tsxmodified+18−17View file
@@ -95,8 +95,9 @@ export default function App() {
9595 </p>
9696 </header>
9797
98- <section className="card">
99- <h2>Model</h2>
98+ {/* The controls stay pinned so the parameters and the ratios they move
99+ are always on screen together, whatever is scrolled to. */}
100+ <section className="card control-bar">
100101 <Controls
101102 sigma={sigma}
102103 setSigma={setSigma}
@@ -109,21 +110,6 @@ export default function App() {
109110 />
110111 </section>
111112
112- <section className="card">
113- <h2>Filter</h2>
114- <FilterViz kernel={kernel} sampleRateHz={sampleRateHz} sigma={sigma} />
115- </section>
116-
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">
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.
124- </p>
125- </section>
126-
127113 <section className="card">
128114 <h2>Compression</h2>
129115 <div className="stat-row">
@@ -169,6 +155,21 @@ export default function App() {
169155 </p>
170156 </section>
171157
158+ <section className="card">
159+ <h2>Quantized signal z</h2>
160+ <ScrollingView kernel={kernel} sigma={sigma} dither={dither} sigmaY={sigmaY} />
161+ <p className="card-note">
162+ A window of samples from the model, drawn from a fixed latent noise sequence — changing
163+ σ, the filter, or dither transforms the same underlying data, so the trace morphs
164+ rather than resampling. Press play to advance through the sequence.
165+ </p>
166+ </section>
167+
168+ <section className="card">
169+ <h2>Filter</h2>
170+ <FilterViz kernel={kernel} sampleRateHz={sampleRateHz} sigma={sigma} />
171+ </section>
172+
172173 <section className="card">
173174 <h2>The math</h2>
174175 <MathSection />
src/app.cssmodified+41−6View file
@@ -47,12 +47,47 @@ body {
4747 .app {
4848 max-width: 1080px;
4949 margin: 0 auto;
50- padding: 24px 20px 64px;
50+ padding: 0 20px 64px;
5151 display: flex;
5252 flex-direction: column;
5353 gap: 16px;
5454 }
5555
56+.app-header {
57+ padding-top: 16px;
58+}
59+
60+/* Pinned parameter bar: the controls follow the reader down the page, so the
61+ numbers they move are never a scroll away. */
62+.control-bar {
63+ position: sticky;
64+ top: 0;
65+ z-index: 20;
66+ padding: 12px 20px;
67+ border-radius: 0 0 10px 10px;
68+ border-top: none;
69+ box-shadow: 0 4px 14px rgba(0, 0, 0, 0.07);
70+}
71+
72+.control-bar .controls {
73+ gap: 10px 22px;
74+}
75+
76+.control-bar .control {
77+ min-width: 0;
78+}
79+
80+.control-bar input[type='range'],
81+.control-bar select {
82+ width: 148px;
83+}
84+
85+@media (prefers-color-scheme: dark) {
86+ .control-bar {
87+ box-shadow: 0 4px 14px rgba(0, 0, 0, 0.45);
88+ }
89+}
90+
5691 .app-header h1 {
5792 font-size: 22px;
5893 font-weight: 650;
@@ -60,9 +95,9 @@ body {
6095 }
6196
6297 .app-header p {
63- margin: 4px 0 0;
98+ margin: 3px 0 0;
6499 color: var(--ink-2);
65- max-width: 72ch;
100+ max-width: 100ch;
66101 }
67102
68103 .card {
@@ -188,7 +223,7 @@ body {
188223 .scroll-canvas {
189224 display: block;
190225 width: 100%;
191- height: 240px;
226+ height: 130px;
192227 }
193228
194229 .play-btn {
@@ -236,8 +271,8 @@ body {
236271 .stat-row {
237272 display: flex;
238273 flex-wrap: wrap;
239- gap: 12px 32px;
240- margin-bottom: 14px;
274+ gap: 10px 32px;
275+ margin-bottom: 12px;
241276 }
242277
243278 .stat {
src/components/CompressionChart.tsxmodified+2−2View file
@@ -9,8 +9,8 @@ const CODER_VARS = ['var(--series-1)', 'var(--series-2)', 'var(--series-3)']
99 const LABEL_W = 96
1010 const RIGHT_PAD = 64
1111 const AXIS_H = 26
12-const GROUP_H = 22
13-const ROW_H = 26
12+const GROUP_H = 20
13+const ROW_H = 24
1414 const BAR_H = 16
1515
1616 type Metric = 'bits' | 'ratio'
src/components/FilterViz.tsxmodified+5−4View file
@@ -2,9 +2,10 @@ import { useMemo, useRef, useState } from 'react'
22 import { magnitudeResponse } from '../model/filters'
33 import { useWidth } from './useWidth'
44
5-const MARGIN = { left: 44, right: 12, top: 10, bottom: 26 }
6-const HEIGHT = 190
7-const DB_FLOOR = -100
5+const MARGIN = { left: 44, right: 12, top: 10, bottom: 24 }
6+const HEIGHT = 120
7+const DB_FLOOR = -80
8+const DB_TICKS = [0, -20, -40, -60, -80]
89 const RESPONSE_POINTS = 512
910
1011 interface Tip {
@@ -166,7 +167,7 @@ function ResponsePanel({
166167 <div className="panel" ref={ref}>
167168 <h3>Frequency response |H(f)|</h3>
168169 <svg width={width} height={HEIGHT} onPointerMove={onMove} onPointerLeave={() => setTip(null)}>
169- {[0, -20, -40, -60, -80, -100].map(v => (
170+ {DB_TICKS.map(v => (
170171 <g key={v}>
171172 <line x1={MARGIN.left} x2={width - MARGIN.right} y1={yOf(v)} y2={yOf(v)} stroke="var(--grid)" strokeWidth={1} />
172173 <text x={MARGIN.left - 6} y={yOf(v) + 4} textAnchor="end" className="axis-tick">
moveopenescclose