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><circle cx=%2250%22 cy=%2250%22 r=%2245%22 fill=%22%232a7f62%22/><circle cx=%2235%22 cy=%2238%22 r=%2211%22 fill=%22%23f5d547%22/><circle cx=%2265%22 cy=%2258%22 r=%229%22 fill=%22%23f5d547%22/><circle cx=%2248%22 cy=%2274%22 r=%227%22 fill=%22%23f5d547%22/><circle cx=%2268%22 cy=%2230%22 r=%226%22 fill=%22%23f5d547%22/></svg>" />
7 <title>turing-sphere — reaction-diffusion on the sphere, 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 --sphere-bg: #f4f6f8;
17 --tok-str: #0a3069;
18 --tok-num: #0550ae;
19 --tok-kw: #cf222e;
20 --tok-ext: #8250df;
2dedc35turing-sphere: reaction-diffusion on the sphere, spectral solver on WebGPUJeremy Magland 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 --sphere-bg: #14161c;
32 --tok-str: #a5d6ff;
33 --tok-num: #79c0ff;
34 --tok-kw: #ff7b72;
35 --tok-ext: #d2a8ff;
2dedc35turing-sphere: reaction-diffusion on the sphere, spectral solver on WebGPUJeremy Magland 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: 6px 0;
51 }
f168295Movie export: recompute the run from t = 0 into a captioned MP4Jeremy Magland 52 /* display:flex above would otherwise override the UA's [hidden] rule */
53 .controls[hidden] { display: none; }
2dedc35turing-sphere: reaction-diffusion on the sphere, spectral solver on WebGPUJeremy Magland 54 .controls label { color: var(--ink-2); font-size: 13px; white-space: nowrap; }
55 select, input[type="number"], button {
56 font: inherit; font-size: 13px;
57 color: var(--ink); background: var(--bg);
58 border: 1px solid var(--line); border-radius: 6px;
59 padding: 4px 8px;
60 }
61 input[type="number"] { width: 6em; }
62 button { cursor: pointer; }
63 button:hover { border-color: var(--accent); }
64 button.primary { border-color: var(--accent); color: var(--accent); font-weight: 600; min-width: 5.5em; }
65 #panels {
66 display: flex; flex-wrap: wrap; gap: 14px; margin-top: 12px;
67 }
68 .panel {
69 flex: 1 1 320px; min-width: 280px;
70 border: 1px solid var(--line); border-radius: 8px; overflow: hidden;
71 display: flex;
72 }
73 .sphere-box { flex: 1; aspect-ratio: 1 / 1; max-height: 70vh; position: relative; }
74 .species-tag {
75 position: absolute; top: 8px; left: 10px; z-index: 2;
76 font-size: 15px; font-weight: 600; color: #fff;
77 background: rgba(0, 0, 0, 0.45);
78 padding: 1px 10px; border-radius: 12px;
79 pointer-events: none;
80 }
81 .colorbar {
82 display: flex; flex-direction: column; align-items: center; justify-content: center;
83 gap: 4px; padding: 8px 4px; background: var(--sphere-bg);
84 width: 52px; flex: none; box-sizing: border-box;
85 }
86 .colorbar canvas { border: 1px solid var(--line); border-radius: 2px; }
87 .colorbar-label { font-size: 11px; color: var(--ink-2); font-variant-numeric: tabular-nums; }
88 .stats { margin-top: 10px; font-size: 13px; color: var(--ink-2); font-variant-numeric: tabular-nums; }
89 .stats b { color: var(--ink); font-weight: 600; }
91 margin-top: 10px; border: 1px solid var(--line); border-radius: 8px;
92 overflow: hidden;
93 }
94 .cli-head {
95 display: flex; gap: 10px; align-items: center; justify-content: space-between;
96 padding: 6px 10px; font-size: 12px; color: var(--ink-2);
97 background: var(--sphere-bg); border-bottom: 1px solid var(--line);
98 }
99 .cli-head button { padding: 2px 10px; font-size: 12px; }
100 #cmd {
101 display: block; padding: 8px 10px; white-space: pre-wrap;
102 font: 12px/1.6 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
103 color: var(--ink); user-select: all;
104 }
2dedc35turing-sphere: reaction-diffusion on the sphere, spectral solver on WebGPUJeremy Magland 105 #blurb { margin-top: 4px; font-size: 13px; color: var(--ink-2); }
106 #err { color: #b35900; white-space: pre-wrap; font-size: 13px; }
108 margin-top: 12px; border: 1px solid var(--line); border-radius: 8px;
109 overflow: hidden;
110 }
111 .editor-head {
112 display: flex; gap: 10px; align-items: center; justify-content: space-between;
113 padding: 6px 10px; font-size: 12px; color: var(--ink-2);
114 background: var(--sphere-bg); border-bottom: 1px solid var(--line);
115 }
116 .editor-head button { padding: 2px 10px; font-size: 12px; }
117 /* Source and compiled-op list side by side, so the editor gets the
118 height rather than sharing it with the list below. */
119 .editor-body { display: flex; align-items: stretch; }
120 .editor-code { position: relative; flex: 1 1 62%; min-width: 0; height: 34em; }
121 /* The overlay and the textarea must agree on every metric that affects
122 where a character lands. Keep these two rules together. */
123 .editor-code > pre,
124 .editor-code > textarea {
125 margin: 0; padding: 10px 12px; border: 0;
126 box-sizing: border-box; width: 100%; height: 100%;
127 font: 12.5px/1.6 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
128 tab-size: 2;
129 white-space: pre; overflow-wrap: normal;
130 }
131 #highlight {
132 position: absolute; inset: 0; overflow: hidden;
133 pointer-events: none; background: var(--bg); color: var(--ink);
134 }
135 #source {
136 position: relative; z-index: 1; display: block;
137 resize: none; overflow: auto;
138 background: transparent; color: transparent; caret-color: var(--ink);
139 }
140 #source:focus { outline: none; }
141 /* Transparent text means the selection must be see-through, or selected
142 code would be invisible. */
143 #source::selection { background: color-mix(in srgb, var(--accent) 28%, transparent); }
144 #compiled {
145 flex: 1 1 38%; min-width: 0; margin: 0; padding: 10px 12px; overflow: auto;
146 border-left: 1px solid var(--line); background: var(--sphere-bg);
147 font: 12px/1.6 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
148 color: var(--ink-2); white-space: pre;
149 }
150 @media (max-width: 860px) {
151 .editor-body { flex-direction: column; }
152 .editor-code { flex: none; height: 26em; }
153 #compiled { border-left: 0; border-top: 1px solid var(--line); max-height: 12em; }
154 }
155 .tok-com { color: var(--tok-com); }
156 .tok-str { color: var(--tok-str); }
157 .tok-num { color: var(--tok-num); }
158 .tok-kw { color: var(--tok-kw); font-weight: 600; }
159 .tok-ext { color: var(--tok-ext); }
2dedc35turing-sphere: reaction-diffusion on the sphere, spectral solver on WebGPUJeremy Magland 160 </style>
161 </head>
162 <body>
163 <main>
164 <h1>turing-sphere</h1>
165 <p class="sub">
166 Reaction-diffusion on the sphere, solved live with spherical harmonics:
167 implicit spectral diffusion + explicit reaction (IMEX Euler), transforms on WebGPU via
168 <a href="https://github.com/concept-collection/shtns-webgpu">shtns-webgpu</a>.
61e12f1Write the solver in MATLAB and compile it to WebGPUJeremy Magland 169 The solver itself is the MATLAB below — <a href="https://numbl.org">numbl</a>
170 parses and lowers it in your browser, and each line becomes a WebGPU
171 compute kernel. Edit it and the pattern changes. Drag to rotate.
2dedc35turing-sphere: reaction-diffusion on the sphere, spectral solver on WebGPUJeremy Magland 172 </p>
173 <div class="controls">
174 <label>preset
175 <select id="model"></select>
176 </label>
177 <label>lmax
178 <select id="lmax">
179 <option value="31">31</option>
180 <option value="63" selected>63</option>
181 <option value="127">127</option>
182 <option value="255">255</option>
183 </select>
184 </label>
163ec45Render on demand, display oversampling, and jump-free solver timingJeremy Magland 185 <label title="Display only — the solution is evaluated on a finer grid for rendering; the solver and its grid are unchanged. Auto oversamples coarse grids and leaves fine ones alone.">display oversampling
186 <select id="oversample">
187 <option value="auto" selected>auto</option>
188 <option value="1">off</option>
189 <option value="2">2×</option>
190 <option value="4">4×</option>
163ec45Render on demand, display oversampling, and jump-free solver timingJeremy Magland 192 </select>
193 </label>
2dedc35turing-sphere: reaction-diffusion on the sphere, spectral solver on WebGPUJeremy Magland 194 <label>colormap
195 <select id="colormap"></select>
196 </label>
197 <button id="runpause" class="primary">Run</button>
3221abdAdd a Benchmark button that measures the solver and the GPU's clock rampJeremy Magland 198 <button id="benchmark">Benchmark</button>
2dedc35turing-sphere: reaction-diffusion on the sphere, spectral solver on WebGPUJeremy Magland 199 <button id="reseed">Re-seed</button>
200 <button id="resetview">Reset view</button>
f168295Movie export: recompute the run from t = 0 into a captioned MP4Jeremy Magland 201 <button id="movietoggle" title="Export the run as an MP4 movie">Export movie</button>
202 </div>
203 <div class="controls" id="moviebar" hidden>
204 <label title="Playback speed: simulation-time units per second of video — 1× plays one time unit per second">movie speed
205 <select id="moviespeed">
206 <option value="0.1">0.1×</option>
207 <option value="0.5">0.5×</option>
208 <option value="1">1×</option>
209 <option value="3">3×</option>
210 <option value="5">5×</option>
211 <option value="10" selected>10×</option>
212 <option value="20">20×</option>
213 </select>
214 </label>
36f75b3Movie export: selectable output resolutionJeremy Magland 215 <label title="Rendered size of each sphere panel, in pixels — the video frame is the panels side by side plus the caption. Independent of the window size.">resolution
216 <select id="movieres">
217 <option value="480">480</option>
218 <option value="640">640</option>
219 <option value="768" selected>768</option>
220 <option value="1080">1080</option>
221 <option value="1440">1440</option>
222 </select>
223 </label>
f168295Movie export: recompute the run from t = 0 into a captioned MP4Jeremy Magland 224 <label title="Slowly orbit the camera during the movie — one revolution per 2 minutes of video, starting from the current view (which is restored afterwards)">
225 <input type="checkbox" id="movierotate" checked /> auto-rotate
226 </label>
227 <button id="movie" title="Recompute the run from t = 0 and download it as an MP4 movie">Export</button>
2dedc35turing-sphere: reaction-diffusion on the sphere, spectral solver on WebGPUJeremy Magland 228 </div>
229 <div class="controls" id="params"></div>
230 <div id="panels"></div>
231 <p class="stats" id="stats"></p>
3221abdAdd a Benchmark button that measures the solver and the GPU's clock rampJeremy Magland 232 <p class="stats" id="benchresult"></p>
234 <div class="editor-head">
235 <span id="editor-title">the solver, in MATLAB</span>
236 <span>
237 <button id="recompile" type="button">Recompile</button>
238 <button id="revert" type="button">Revert</button>
239 </span>
240 </div>
241 <div class="editor-body">
242 <div class="editor-code">
243 <pre id="highlight" aria-hidden="true"></pre>
244 <textarea
245 id="source"
246 spellcheck="false"
247 autocomplete="off"
248 autocapitalize="off"
249 aria-label="model source (MATLAB)"
250 ></textarea>
251 </div>
252 <pre id="compiled"></pre>
253 </div>
254 </div>
15a77e2Add a desktop WebGPU benchmark and show its command in the appJeremy Magland 255 <div class="cli">
256 <div class="cli-head">
61e12f1Write the solver in MATLAB and compile it to WebGPUJeremy Magland 257 <span>The same parameters on the desktop, via the reference TypeScript solver</span>
15a77e2Add a desktop WebGPU benchmark and show its command in the appJeremy Magland 258 <button id="copycmd" type="button">Copy</button>
259 </div>
260 <code id="cmd"></code>
261 </div>
2dedc35turing-sphere: reaction-diffusion on the sphere, spectral solver on WebGPUJeremy Magland 262 <p id="blurb"></p>
263 <p id="err"></p>
264 </main>
265 <script type="module" src="/src/main.ts"></script>
266 </body>
267</html>