/ concept-collection / acoustic-scattering-2d
Sign in
concept-collection / acoustic-scattering-2d
acoustic-scattering-2d / index.html
266 lines · 11.4 KBBlameHistoryRaw
1<!doctype html>
2<html lang="en">
3 <head>
4 <meta charset="utf-8" />
5 <meta name="viewport" content="width=device-width, initial-scale=1" />
6 <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><rect width=%22100%22 height=%22100%22 fill=%22%23f2f2f2%22/><circle cx=%2258%22 cy=%2250%22 r=%2216%22 fill=%22%23888%22/><path d=%22M8 20v60M22 14v72M36 20v60%22 stroke=%22%233b4cc0%22 stroke-width=%228%22 fill=%22none%22/><path d=%22M76 30a24 24 0 0 1 0 40%22 stroke=%22%23b40426%22 stroke-width=%227%22 fill=%22none%22/></svg>" />
7 <title>acoustic-scattering-2d — 2D scattering, live in the browser</title>
8 <style>
9 :root {
10 --bg: #ffffff;
11 --ink: #1f2328;
12 --ink-2: #57606a;
13 --line: #d0d7de;
14 --accent: #0969da;
15 --panel-bg: #f4f6f8;
16 --tok-com: #6e7781;
17 --tok-str: #0a3069;
18 --tok-num: #0550ae;
19 --tok-kw: #cf222e;
20 --tok-ext: #8250df;
21 color-scheme: light dark;
22 }
23 @media (prefers-color-scheme: dark) {
24 :root {
25 --bg: #14171a;
26 --ink: #e6e9ec;
27 --ink-2: #9aa4af;
28 --line: #333b44;
29 --accent: #58a6ff;
30 --panel-bg: #14161c;
31 --tok-com: #8b949e;
32 --tok-str: #a5d6ff;
33 --tok-num: #79c0ff;
34 --tok-kw: #ff7b72;
35 --tok-ext: #d2a8ff;
36 }
37 }
38 body {
39 margin: 0;
40 background: var(--bg);
41 color: var(--ink);
42 font: 15px/1.5 system-ui, -apple-system, sans-serif;
43 }
44 main { max-width: 1100px; margin: 0 auto; padding: 20px 16px 48px; }
45 h1 { font-size: 20px; margin: 0 0 2px; }
46 .sub { color: var(--ink-2); margin: 0 0 12px; font-size: 13px; }
47 .sub a { color: var(--accent); }
48 .controls {
49 display: flex; flex-wrap: wrap; gap: 8px 14px; align-items: center;
50 padding: 5px 0;
51 }
52 .controls label { color: var(--ink-2); font-size: 13px; white-space: nowrap; }
53 select, input[type="number"], button {
54 font: inherit; font-size: 13px;
55 color: var(--ink); background: var(--bg);
56 border: 1px solid var(--line); border-radius: 6px;
57 padding: 4px 8px;
58 }
59 input[type="range"] { width: 8em; vertical-align: middle; accent-color: var(--accent); }
60 button { cursor: pointer; }
61 button:hover { border-color: var(--accent); }
62 button.primary { border-color: var(--accent); color: var(--accent); font-weight: 600; min-width: 5.5em; }
63 .sliders {
64 display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
65 gap: 2px 16px; padding: 4px 0;
66 }
67 #micbar { gap: 8px 16px; }
68 #micparams { display: flex; gap: 16px; padding: 0; }
69 #recinfo { margin-top: 0; }
70 .slider { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--ink-2); }
71 .slider > span:first-child { flex: 0 0 8.5em; text-align: right; }
72 .slider > output { flex: 0 0 4em; font-variant-numeric: tabular-nums; color: var(--ink); }
73 .group-title {
74 font-size: 12px; text-transform: uppercase; letter-spacing: 0.05em;
75 color: var(--ink-2); margin: 10px 0 0;
76 }
77 #stage {
78 display: flex; gap: 14px; margin-top: 12px; align-items: stretch;
79 border: 1px solid var(--line); border-radius: 8px; overflow: hidden;
80 }
81 .canvas-box { flex: 1; aspect-ratio: 1 / 1; max-height: 70vh; position: relative; background: var(--panel-bg); }
82 #view { width: 100%; height: 100%; display: block; cursor: crosshair; touch-action: none; }
83 .colorbar {
84 display: flex; flex-direction: column; align-items: center; justify-content: center;
85 gap: 4px; padding: 8px 4px; background: var(--panel-bg);
86 width: 60px; flex: none; box-sizing: border-box;
87 }
88 .colorbar canvas { border: 1px solid var(--line); border-radius: 2px; }
89 .colorbar-label { font-size: 11px; color: var(--ink-2); font-variant-numeric: tabular-nums; }
90 .readout { font-variant-numeric: tabular-nums; color: var(--ink); }
91 .readout.unstable, .warn { color: #b35900; font-weight: 600; }
92 .stats { margin-top: 8px; font-size: 13px; color: var(--ink-2); font-variant-numeric: tabular-nums; }
93 .stats b { color: var(--ink); font-weight: 600; }
94 #blurb { margin-top: 6px; font-size: 13px; color: var(--ink-2); }
95 #err {
96 color: #b35900; white-space: pre-wrap; font-size: 13px;
97 font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
98 }
99 .editor {
100 margin-top: 12px; border: 1px solid var(--line); border-radius: 8px;
101 overflow: hidden;
102 }
103 .editor-head {
104 display: flex; gap: 10px; align-items: center; justify-content: space-between;
105 padding: 6px 10px; font-size: 12px; color: var(--ink-2);
106 background: var(--panel-bg); border-bottom: 1px solid var(--line);
107 }
108 .editor-head button { padding: 2px 10px; font-size: 12px; }
109 /* The fixed height lives on the row, not on either child: sizing the row
110 makes both children stretch to one shared pixel height regardless of
111 either's font size. */
112 .editor-body { display: flex; align-items: stretch; height: 32em; }
113 .editor-code { position: relative; flex: 1 1 62%; min-width: 0; }
114 /* The overlay and the textarea must agree on every metric that affects
115 where a character lands. Keep these two rules together. */
116 .editor-code > pre,
117 .editor-code > textarea {
118 margin: 0; padding: 10px 12px; border: 0;
119 box-sizing: border-box; width: 100%; height: 100%;
120 font: 12.5px/1.6 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
121 tab-size: 2;
122 white-space: pre; overflow-wrap: normal;
123 }
124 #highlight {
125 position: absolute; inset: 0; overflow: hidden;
126 pointer-events: none; background: var(--bg); color: var(--ink);
127 }
128 #source {
129 position: relative; z-index: 1; display: block;
130 resize: none; overflow: auto;
131 background: transparent; color: transparent; caret-color: var(--ink);
132 }
133 #source:focus { outline: none; }
134 /* Transparent text means the selection must be see-through, or selected
135 code would be invisible. */
136 #source::selection { background: color-mix(in srgb, var(--accent) 28%, transparent); }
137 #compiled {
138 flex: 1 1 38%; min-width: 0; margin: 0; padding: 10px 12px; overflow: auto;
139 border-left: 1px solid var(--line); background: var(--panel-bg);
140 font: 12px/1.6 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
141 color: var(--ink-2); white-space: pre;
142 }
143 @media (max-width: 860px) {
144 .editor-body { flex-direction: column; height: auto; }
145 .editor-code { flex: none; height: 26em; }
146 #compiled { border-left: 0; border-top: 1px solid var(--line); max-height: 12em; }
147 }
148 .tok-com { color: var(--tok-com); }
149 .tok-str { color: var(--tok-str); }
150 .tok-num { color: var(--tok-num); }
151 .tok-kw { color: var(--tok-kw); font-weight: 600; }
152 .tok-ext { color: var(--tok-ext); }
153 </style>
154 </head>
155 <body>
156 <main>
157 <h1>acoustic-scattering-2d</h1>
158 <p class="sub">
159 Sound scattering off obstacles in two dimensions, solved live on your
160 GPU. The wave equation and the medium are both the MATLAB below, run in
161 your browser by <a href="https://numbl.org">numbl</a> and compiled to
162 WebGPU compute shaders. Edit either and watch it change.
163 </p>
164 <p class="sub" id="domaininfo"></p>
166 <div class="controls">
167 <label>scene
168 <select id="scene"></select>
169 </label>
170 <label title="The time-stepping scheme. Changing it recompiles.">model
171 <select id="model"></select>
172 </label>
173 <label title="Grid points per side. Changing it recompiles and restarts.">grid
174 <select id="gridsize">
175 <option value="128">128²</option>
176 <option value="256">256²</option>
177 <option value="384">384²</option>
178 <option value="512" selected>512²</option>
179 </select>
180 </label>
181 <button id="runpause" class="primary">Run</button>
182 <button id="restart" title="Back to a silent grid at t = 0">Restart</button>
183 </div>
185 <p class="group-title">source</p>
186 <div class="sliders" id="params"></div>
187 <p class="group-title" id="scene-title">scene</p>
188 <div class="sliders" id="sceneparams"></div>
190 <div class="controls">
191 <label>colormap
192 <select id="colormap"></select>
193 </label>
194 <label title="Pressure the colour scale saturates at. Auto follows the field.">scale
195 <select id="scalemode">
196 <option value="auto" selected>auto</option>
197 <option value="fixed">hold</option>
198 </select>
199 </label>
200 <label title="Shade the sound-speed field underneath the wave">
201 <input type="checkbox" id="showmedium" checked /> show medium
202 </label>
203 <label id="cfl-label" title="The timestep, as a fraction of the largest one this scheme is stable at on this grid. At 1 and above it diverges, which is worth seeing once.">timestep
204 <input type="range" id="cfl" min="0.05" max="1.2" step="0.05" value="0.5" />
205 <output id="dtout" class="readout"></output>
206 </label>
207 <label title="Timesteps taken between frames">speed
208 <select id="spf">
209 <option value="1"></option>
210 <option value="2"></option>
211 <option value="4" selected></option>
212 <option value="8"></option>
213 <option value="16">16×</option>
214 <option value="32">32×</option>
215 <option value="64">64×</option>
216 </select>
217 </label>
218 </div>
220 <p class="group-title">microphone — click or drag on the picture to move it</p>
221 <div class="controls" id="micbar">
222 <div class="sliders" id="micparams"></div>
223 <button id="listen" title="Play what the microphone has recorded since the run started, in real time at its real pitch">Listen</button>
224 <span class="stats" id="recinfo"></span>
225 </div>
227 <div id="stage">
228 <div class="canvas-box"><canvas id="view"></canvas></div>
229 <div id="colorbar"></div>
230 </div>
231 <p class="stats" id="stats"></p>
232 <p id="blurb"></p>
233 <p id="err"></p>
235 <div class="editor">
236 <div class="editor-head">
237 <span>
238 <select id="editor-file" aria-label="file to edit">
239 <option value="model">model (.m) — the solver</option>
240 <option value="scene">scene (.m) — the medium</option>
241 </select>
242 <span id="editor-title"></span>
243 </span>
244 <span>
245 <button id="recompile" type="button">Run edits</button>
246 <button id="revert" type="button">Revert</button>
247 </span>
248 </div>
249 <div class="editor-body">
250 <div class="editor-code">
251 <pre id="highlight" aria-hidden="true"></pre>
252 <textarea
253 id="source"
254 spellcheck="false"
255 autocomplete="off"
256 autocapitalize="off"
257 aria-label="source (MATLAB)"
258 ></textarea>
259 </div>
260 <pre id="compiled"></pre>
261 </div>
262 </div>
263 </main>
264 <script type="module" src="/src/main.ts"></script>
265 </body>
266</html>
moveopenescclose