/ concept-collection / acoustic-scattering-3d
Sign in
concept-collection / acoustic-scattering-3d
acoustic-scattering-3d / index.html
180 lines · 7.9 KBCodeBlameHistory
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%230b0e12%22/><path d=%22M18 26h44l20 14v34H38L18 60z%22 fill=%22none%22 stroke=%22%236b7684%22 stroke-width=%224%22/><circle cx=%2248%22 cy=%2252%22 r=%2213%22 fill=%22%23b40426%22/><circle cx=%2248%22 cy=%2252%22 r=%2222%22 fill=%22none%22 stroke=%22%233b4cc0%22 stroke-width=%225%22/></svg>" />
7 <title>acoustic-scattering-3d — 3D 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 color-scheme: light dark;
17 }
18 @media (prefers-color-scheme: dark) {
19 :root {
20 --bg: #14171a;
21 --ink: #e6e9ec;
22 --ink-2: #9aa4af;
23 --line: #333b44;
24 --accent: #58a6ff;
25 --panel-bg: #14161c;
26 }
27 }
28 body {
29 margin: 0;
30 background: var(--bg);
31 color: var(--ink);
32 font: 15px/1.5 system-ui, -apple-system, sans-serif;
33 }
34 main { max-width: 1100px; margin: 0 auto; padding: 20px 16px 48px; }
35 h1 { font-size: 20px; margin: 0 0 2px; }
36 .sub { color: var(--ink-2); margin: 0 0 12px; font-size: 13px; }
37 .sub a { color: var(--accent); }
38 .controls {
39 display: flex; flex-wrap: wrap; gap: 8px 14px; align-items: center;
40 padding: 5px 0;
41 }
42 .controls label { color: var(--ink-2); font-size: 13px; white-space: nowrap; }
43 select, input[type="number"], button {
44 font: inherit; font-size: 13px;
45 color: var(--ink); background: var(--bg);
46 border: 1px solid var(--line); border-radius: 6px;
47 padding: 4px 8px;
48 }
49 input[type="range"] { width: 8em; vertical-align: middle; accent-color: var(--accent); }
50 button { cursor: pointer; }
51 button:hover { border-color: var(--accent); }
52 button.primary { border-color: var(--accent); color: var(--accent); font-weight: 600; min-width: 5.5em; }
53 .sliders {
54 display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
55 gap: 2px 16px; padding: 4px 0;
56 }
57 .slider { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--ink-2); }
58 .slider > span:first-child { flex: 0 0 8.5em; text-align: right; }
59 .slider > output { flex: 0 0 4.5em; font-variant-numeric: tabular-nums; color: var(--ink); }
60 .group-title {
61 font-size: 12px; text-transform: uppercase; letter-spacing: 0.05em;
62 color: var(--ink-2); margin: 10px 0 0;
63 }
64 #micbar { gap: 8px 16px; }
65 #micparams { display: flex; flex-wrap: wrap; gap: 2px 16px; padding: 0; }
66 #recinfo { margin-top: 0; }
67 #stage {
68 display: flex; gap: 0; margin-top: 12px; align-items: stretch;
69 border: 1px solid var(--line); border-radius: 8px; overflow: hidden;
70 background: #0b0e12;
71 }
72 .canvas-box { flex: 1; aspect-ratio: 1 / 1; max-height: 70vh; position: relative; }
73 #view { width: 100%; height: 100%; display: block; cursor: grab; touch-action: none; }
74 #view:active { cursor: grabbing; }
75 .colorbar {
76 display: flex; flex-direction: column; align-items: center; justify-content: center;
77 gap: 4px; padding: 8px 6px; width: 64px; flex: none; box-sizing: border-box;
78 color: #9aa4af;
79 }
80 .colorbar canvas { border: 1px solid #333b44; border-radius: 2px; }
81 .colorbar-label { font-size: 11px; font-variant-numeric: tabular-nums; }
82 .readout { font-variant-numeric: tabular-nums; color: var(--ink); }
83 .readout.unstable, .warn { color: #b35900; font-weight: 600; }
84 .stats { margin-top: 8px; font-size: 13px; color: var(--ink-2); font-variant-numeric: tabular-nums; }
85 .stats b { color: var(--ink); font-weight: 600; }
86 #blurb { margin-top: 6px; font-size: 13px; color: var(--ink-2); }
87 #err {
88 color: #b35900; white-space: pre-wrap; font-size: 13px;
89 font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
90 }
91 </style>
92 </head>
93 <body>
94 <main>
95 <h1>acoustic-scattering-3d</h1>
96 <p class="sub">
97 Sound scattering off obstacles in three dimensions, solved live on your
98 GPU: a leapfrog on a cubic grid, drawn by marching a ray through the
99 pressure buffer the solver just wrote. Drag to turn the cube, scroll to
100 zoom, and pull the clip plane in to see inside it. The flat sibling is
101 <a href="https://github.com/concept-collection/acoustic-scattering-2d">acoustic-scattering-2d</a>.
102 </p>
103 <p class="sub" id="domaininfo"></p>
105 <div class="controls">
106 <label>scene
107 <select id="scene"></select>
108 </label>
109 <label title="Grid points per side. Changing it restarts the run.">grid
110 <select id="gridsize"></select>
111 </label>
112 <button id="runpause" class="primary">Run</button>
113 <button id="restart" title="Back to a silent grid at t = 0">Restart</button>
114 </div>
116 <p class="group-title">source</p>
117 <div class="sliders" id="params"></div>
118 <p class="group-title" id="scene-title">scene</p>
119 <div class="sliders" id="sceneparams"></div>
121 <div class="controls">
122 <label>colormap
123 <select id="colormap"></select>
124 </label>
125 <label title="Pressure the colour scale saturates at. Auto follows the field; hold keeps the value it had when you switched.">scale
126 <select id="scalemode">
127 <option value="auto">auto</option>
128 <option value="fixed" selected>hold</option>
129 </select>
130 </label>
131 <label title="Shade the sound-speed field alongside the wave">
132 <input type="checkbox" id="showmedium" checked /> show medium
133 </label>
134 <label title="Samples along each ray. Fewer is faster and grainier.">quality
135 <select id="quality">
136 <option value="96">low</option>
137 <option value="192" selected>medium</option>
138 <option value="320">high</option>
139 </select>
140 </label>
141 <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
142 <input type="range" id="cfl" min="0.05" max="1.2" step="0.05" value="0.5" />
143 <output id="dtout" class="readout"></output>
144 </label>
145 <label title="Timesteps taken between frames">speed
146 <select id="spf">
147 <option value="1"></option>
148 <option value="2"></option>
149 <option value="4" selected></option>
150 <option value="8"></option>
151 <option value="16">16×</option>
152 <option value="32">32×</option>
153 <option value="64">64×</option>
154 </select>
155 </label>
156 </div>
157 <div class="sliders" id="viewparams"></div>
159 <p class="group-title">microphone</p>
160 <div class="controls" id="micbar">
161 <div class="sliders" id="micparams"></div>
162 <button id="listen" title="Play what the microphone has recorded since the run started, in real time">Listen</button>
163 <span class="stats" id="recinfo"></span>
164 </div>
166 <div id="stage">
167 <div class="canvas-box"><canvas id="view"></canvas></div>
168 <div class="colorbar" id="colorbar">
169 <span class="colorbar-label" id="cbhi"></span>
170 <canvas id="cbar" width="14" height="220"></canvas>
171 <span class="colorbar-label" id="cblo"></span>
172 </div>
173 </div>
174 <p class="stats" id="stats"></p>
175 <p id="blurb"></p>
176 <p id="err"></p>
177 </main>
178 <script type="module" src="/src/main.ts"></script>
179 </body>
180</html>
moveopenescclose