/ listening-for-chaos / chaos-apps
Sign in
listening-for-chaos / chaos-apps
One multi-page Vite site: shared config at the root, landing page, apps at /double-pendulum/ and /fput-chain/
Jeremy Magland <jmagland@flatironinstitute.org> committed commit d1459b91cc2f parent 988367f Browse files
19 changed files+100−1314
src/double-pendulum/.github/workflows/pages.yml →.github/workflows/pages.ymlrenamed+0−0View file
No changes to the file's content.
src/double-pendulum/.gitignore →.gitignorerenamed+0−0View file
No changes to the file's content.
README.mdadded+18−0View file
@@ -0,0 +1,18 @@
1+# chaos-apps
2+
3+Interactive apps for "Listening for chaos": struck dynamical systems heard as sound while they decay. One Vite multi-page site; each app lives in its own folder with its own README.
4+
5+- `src/double-pendulum/`: double pendulum ([README](src/double-pendulum/README.md)), served at `/double-pendulum/`.
6+- `src/fput-chain/`: FPUT-beta chain ([README](src/fput-chain/README.md)), served at `/fput-chain/`.
7+- `src/index.html`: landing page linking the apps.
8+
9+Live site: https://chaos-apps--listening-for-chaos.vault1-sites.magland.org/ (built and deployed by `.github/workflows/pages.yml` on every push to main).
10+
11+The apps were separate repos (`double-pendulum-chaos`, `fput-chain-chaos`) until 2026-10-09; their histories are merged here, each moved into its folder.
12+
13+```bash
14+npm install
15+npm run dev
16+```
17+
18+The dev server serves every app (http://localhost:5173/double-pendulum/, http://localhost:5173/fput-chain/); edits anywhere under `src/` reload the open pages.
src/fput-chain/package-lock.json →package-lock.jsonrenamed+2−2View file
@@ -1,10 +1,10 @@
11 {
2- "name": "fput-chain-chaos",
2+ "name": "chaos-apps",
33 "lockfileVersion": 3,
44 "requires": true,
55 "packages": {
66 "": {
7- "name": "fput-chain-chaos",
7+ "name": "chaos-apps",
88 "devDependencies": {
99 "typescript": "^7.0.2",
1010 "vite": "^8.3.4"
src/fput-chain/package.json →package.jsonrenamed+1−1View file
@@ -1,5 +1,5 @@
11 {
2- "name": "fput-chain-chaos",
2+ "name": "chaos-apps",
33 "private": true,
44 "type": "module",
55 "scripts": {
src/double-pendulum/README.mdmodified+7−11View file
@@ -1,15 +1,11 @@
1-# double-pendulum-chaos
1+# double-pendulum
22
33 Interactive double pendulum, step 1 of "Listening for chaos". Work in progress.
44
5-Current state: the pendulum hangs at rest until Strike, which starts it from hanging rest with the energy on the slider (default 50 J), plus a random relative jitter of up to 1e-9 in each angle and momentum (so chaotic strikes differ from one another while regular ones sound alike); Pause (or the space bar) pauses everything: the animation and the sound (the audio clock is suspended, so the note and every display fed by it stop where they are); Resume or Strike continues. The Ghost toggle adds a second pendulum (translucent orange) started from the same state with the outer arm's angle offset by 10^-12 to 10^-1 rad (default 10^-10), restarted on Strike or "Restart ghost", with a readout of the angular separation and the time since the ghost started: it grows exponentially when the motion is chaotic and stays tiny when it is regular. Charts "Separation" (ghost separation, log scale, last 30 s; its chip shows only with the ghost on) and "Lyapunov" (largest Lyapunov exponent over a 10 s window and since the strike, last 30 s; the first 5 s after a strike, where the values are large, are left out of the y range, here and in the sound's Lyapunov chart). Chart legends show 4 significant digits, ranges 3. Parameters (masses, lengths, g, decay time, presets) fold out underneath. The plots card is labelled "Real time" and the "Sped up · listen" panel's header says how much faster its run is (e.g. the whole decay, 262x faster: 62 min of motion in 14 s), since the two run on different clocks; the Follow options read "real time" and "sped up". Toggle chips choose the plots: live traces of energies, energy balance error, angles, momenta and bob positions. Each chart shows its time span at the bottom right ("last 10 s" when scrolling; the note length in the sound panel). The Poincare section panel (closed by default) has its own energy slider (default 17 J) and uses the same masses and lengths, without damping: each time the inner arm swings down through vertical moving right, the outer arm's angle and momentum are plotted. 42 orbits start on a grid inside the allowed region (outlined) and leave 300 dots each, computed in a Web Worker and drawn as they arrive; regular orbits trace closed curves, chaotic ones scatter. Hover to pick out an orbit (with its Lyapunov exponent), click inside the outline to start a new orbit there (with "Click to strike" on, the default, this also strikes the pendulum above from that exact state, at the section's energy, without jitter). The animated pendulum's own crossings are drawn as dark rings (the latest larger), so you can see which orbit it is on; only those within 5% of the section's energy are drawn (others would fall outside the allowed region; the legend then says "pendulum at 48 J, not shown"), and they clear on each strike. Follow stays selected; setting the section's energy by hand pauses everything, clears the recent crossings (rings and note trail) and shows the section at that energy, and Follow takes over again on resume. Follow: "real time" makes the section's energy track the pendulum's as it decays (recomputed after a 3% change, at most every 2 s); "sped up" (which opens the "Sped up · listen" panel) shows the playing note's crossings (sent by the audio worker with the samples, drawn as the audio clock reaches them) as a fading trail of the last 150, with the axes fitted to the note's current energy (zooming as it decays) and nothing computed live. Without an atlas only the outline is drawn behind the trail; with one, the atlas section nearest the current energy, fitted to the current allowed region (each point scaled by the ratio of the regions' extents in th2 and p2: exact at low energy, where sections are scaled copies, an approximation over the <~15% gaps between atlas energies). Trail dots are fitted the same way from their own energies. "Precompute sections" (shown only with Follow "sped up", until the atlas is complete) builds the atlas (`src/atlas.ts`): sections at 24 energies log-spaced from 0.4% of to twice the both-inverted energy, 42 orbits x 150 crossings each, highest energy first, in its own worker (~80 s in the desktop app's browser pane), stored in IndexedDB per parameter set and deleted after 7 days. "Color by λ" colors orbits by Lyapunov exponent (log scale, blue regular to red chaotic). The "Sped up · listen" panel (above the section) starts closed; opening it switches the sound engine on (closing it switches it off) without playing anything; a line of text says to Strike (or click the section) until the first note. While it is open, every Strike plays a note: a separate, sped-up copy of the motion from the same strike; a damped note ends once it has decayed by 60 dB. Save WAV saves the current or last note as heard (as received from the audio worker, up to 120 s; 16-bit mono PCM, peak at 0.9; `src/wav.ts`; named e.g. double-pendulum-50.0J-x2-100Hz.wav). The mute button (speaker icon; when muted it is tinted red and reads "Muted") starts muted and unmutes the first time the panel is opened; muted notes are still computed and shown, just silent. The panel also has "Signal" (which coordinate is heard), volume, pitch (where the small-amplitude lower mode lands, 30 to 1000 Hz, default 100 Hz; the readout gives both modes' pitches), and optional whole-note displays that do not scroll (strike at the left edge, end of the note at the right): a spectrogram of the sound (frequency axis up to just above the highest frequency that comes within 40 dB of the peak at any time in the note, so the broadband start of a chaotic note stays in view), the largest Lyapunov exponent of the motion being heard (10 s window, physical time), its energy (log scale, so the exponential decay is a straight line), and a scatter of lambda against energy accumulated over notes (log energy axis, since a note spends most of its time at low energy; colored by time into the note, earlier notes faded, Clear button). Spectrogram on by default. Each card and panel (and Parameters) has a small "?" button that opens a short help dialog (`src/help.ts`; the texts are `<template>`s in `index.html`). Hovering over any chart (real time or sped up) shows a time cursor and the values there; a click pins it. Charts in the same card share the cursor. The time axis spans the predicted note length ((tau/2) ln 1e6 of physical time, divided by the speedup), is extended if the note runs longer, and is fitted to the note when it ends.
5+Current state: the pendulum hangs at rest until Strike, which starts it from hanging rest with the energy on the slider (default 50 J), plus a random relative jitter of up to 1e-9 in each angle and momentum (so chaotic strikes differ from one another while regular ones sound alike); Pause (or the space bar) pauses everything: the animation and the sound (the audio clock is suspended, so the note and every display fed by it stop where they are); Resume or Strike continues. The Ghost toggle adds a second pendulum (translucent orange) started from the same state with the outer arm's angle offset by 10^-12 to 10^-1 rad (default 10^-10), restarted on Strike or "Restart ghost", with a readout of the angular separation and the time since the ghost started: it grows exponentially when the motion is chaotic and stays tiny when it is regular. Charts "Separation" (ghost separation, log scale, last 30 s; its chip shows only with the ghost on) and "Lyapunov" (largest Lyapunov exponent over a 10 s window and since the strike, last 30 s; the first 5 s after a strike, where the values are large, are left out of the y range, here and in the sound's Lyapunov chart). Chart legends show 4 significant digits, ranges 3. Parameters (masses, lengths, g, decay time, presets) fold out underneath. The plots card is labelled "Real time" and the "Sped up · listen" panel's header says how much faster its run is (e.g. the whole decay, 262x faster: 62 min of motion in 14 s), since the two run on different clocks; the Follow options read "real time" and "sped up". Toggle chips choose the plots: live traces of energies, energy balance error, angles, momenta and bob positions. Each chart shows its time span at the bottom right ("last 10 s" when scrolling; the note length in the sound panel). The Poincare section panel (closed by default) has its own energy slider (default 17 J) and uses the same masses and lengths, without damping: each time the inner arm swings down through vertical moving right, the outer arm's angle and momentum are plotted. 42 orbits start on a grid inside the allowed region (outlined) and leave 300 dots each, computed in a Web Worker and drawn as they arrive; regular orbits trace closed curves, chaotic ones scatter. Hover to pick out an orbit (with its Lyapunov exponent), click inside the outline to start a new orbit there (with "Click to strike" on, the default, this also strikes the pendulum above from that exact state, at the section's energy, without jitter). The animated pendulum's own crossings are drawn as dark rings (the latest larger), so you can see which orbit it is on; only those within 5% of the section's energy are drawn (others would fall outside the allowed region; the legend then says "pendulum at 48 J, not shown"), and they clear on each strike. Follow stays selected; setting the section's energy by hand pauses everything, clears the recent crossings (rings and note trail) and shows the section at that energy, and Follow takes over again on resume. Follow: "real time" makes the section's energy track the pendulum's as it decays (recomputed after a 3% change, at most every 2 s); "sped up" (which opens the "Sped up · listen" panel) shows the playing note's crossings (sent by the audio worker with the samples, drawn as the audio clock reaches them) as a fading trail of the last 150, with the axes fitted to the note's current energy (zooming as it decays) and nothing computed live. Without an atlas only the outline is drawn behind the trail; with one, the atlas section nearest the current energy, fitted to the current allowed region (each point scaled by the ratio of the regions' extents in th2 and p2: exact at low energy, where sections are scaled copies, an approximation over the <~15% gaps between atlas energies). Trail dots are fitted the same way from their own energies. "Precompute sections" (shown only with Follow "sped up", until the atlas is complete) builds the atlas (`atlas.ts`): sections at 24 energies log-spaced from 0.4% of to twice the both-inverted energy, 42 orbits x 150 crossings each, highest energy first, in its own worker (~80 s in the desktop app's browser pane), stored in IndexedDB per parameter set and deleted after 7 days. "Color by λ" colors orbits by Lyapunov exponent (log scale, blue regular to red chaotic). The "Sped up · listen" panel (above the section) starts closed; opening it switches the sound engine on (closing it switches it off) without playing anything; a line of text says to Strike (or click the section) until the first note. While it is open, every Strike plays a note: a separate, sped-up copy of the motion from the same strike; a damped note ends once it has decayed by 60 dB. Save WAV saves the current or last note as heard (as received from the audio worker, up to 120 s; 16-bit mono PCM, peak at 0.9; `wav.ts`; named e.g. double-pendulum-50.0J-x2-100Hz.wav). The mute button (speaker icon; when muted it is tinted red and reads "Muted") starts muted and unmutes the first time the panel is opened; muted notes are still computed and shown, just silent. The panel also has "Signal" (which coordinate is heard), volume, pitch (where the small-amplitude lower mode lands, 30 to 1000 Hz, default 100 Hz; the readout gives both modes' pitches), and optional whole-note displays that do not scroll (strike at the left edge, end of the note at the right): a spectrogram of the sound (frequency axis up to just above the highest frequency that comes within 40 dB of the peak at any time in the note, so the broadband start of a chaotic note stays in view), the largest Lyapunov exponent of the motion being heard (10 s window, physical time), its energy (log scale, so the exponential decay is a straight line), and a scatter of lambda against energy accumulated over notes (log energy axis, since a note spends most of its time at low energy; colored by time into the note, earlier notes faded, Clear button). Spectrogram on by default. Each card and panel (and Parameters) has a small "?" button that opens a short help dialog (`help.ts`; the texts are `<template>`s in `index.html`). Hovering over any chart (real time or sped up) shows a time cursor and the values there; a click pins it. Charts in the same card share the cursor. The time axis spans the predicted note length ((tau/2) ln 1e6 of physical time, divided by the speedup), is extended if the note runs longer, and is fitted to the note when it ends.
66
7-Live site: https://double-pendulum-chaos--listening-for-chaos.vault1-sites.magland.org/ (built and deployed by `.github/workflows/pages.yml` on every push to main).
7+Live site: https://chaos-apps--listening-for-chaos.vault1-sites.magland.org/double-pendulum/ (part of the chaos-apps site; see the top-level README).
88
9-```bash
10-npm install
11-npm run dev
12-```
139
1410 ## Model
1511
@@ -18,10 +14,10 @@ npm run dev
1814 - Energy is zero at hanging rest.
1915 - Damping: linear drag on each bob, force -gamma m_i v_i (proportional to its mass; equal for equal bobs), so the generalized forces are -gamma p and every mode's amplitude decays with time constant tau = 2/gamma. Default tau = 540 s: about 10% amplitude per minute in the animation, about 2 s in the sound at x262. The state carries the dissipated energy W, so E + W is checked for conservation. With damping the energy slider (labelled "Strike energy") sets the strike energy and the current energy is shown at the top left of the animation; with tau = 0 it is labelled "Energy" and sets the energy live. The section's controls are in two rows (energy and Follow; Color by λ and Click to strike), and "computing…" shows in the plot's top-right legend.
2016 - Live energy change: both momenta are scaled by one factor at fixed angles (a push along the current motion). A target below the current potential energy can't be reached at once; until it can, kinetic energy is removed at a rate of 6 per second.
21-- Lyapunov exponent: two-trajectory (Benettin) method in `src/lyapunov.ts`. A hidden shadow pendulum is kept 1e-8 away (angles in rad, momenta in units of sqrt((m1 + m2) l1^2 E_top)), moved back after every step, and the log growth averaged. The shadow keeps its own energy (forcing it onto the pendulum's energy by rescaling momenta inflates the separation near turning points). Checked (no damping, 300 s): 2 J and 10 J fall toward 0 (0.01/s), 30 J ~1.0/s, 50 J ~1.4/s; unchanged for shadow distances 1e-6 and 1e-10. With damping (tau 540 s) a 50 J strike's 10 s value falls from ~1.4 to ~0.1/s by 300 s.
22-- Poincare section (`src/poincare-core.ts`): on the section th1 = 0, a point (th2, p2) is reachable at energy E exactly when p2^2 <= 2 m2 l2^2 (E - V(th2)), V(th2) = m2 g l2 (1 - cos th2); p1 is the root with the inner arm moving right. Crossings are located by linear interpolation within a step. The step is 4x the animation's: over 300 crossings the energy error stays below ~2e-5, the regular/chaotic classification is unchanged, and regular orbits move by ~1e-4. Each orbit's exponent is the shadow method's mean over its run (regular orbits ~0.01/s or less).
17+- Lyapunov exponent: two-trajectory (Benettin) method in `lyapunov.ts`. A hidden shadow pendulum is kept 1e-8 away (angles in rad, momenta in units of sqrt((m1 + m2) l1^2 E_top)), moved back after every step, and the log growth averaged. The shadow keeps its own energy (forcing it onto the pendulum's energy by rescaling momenta inflates the separation near turning points). Checked (no damping, 300 s): 2 J and 10 J fall toward 0 (0.01/s), 30 J ~1.0/s, 50 J ~1.4/s; unchanged for shadow distances 1e-6 and 1e-10. With damping (tau 540 s) a 50 J strike's 10 s value falls from ~1.4 to ~0.1/s by 300 s.
18+- Poincare section (`poincare-core.ts`): on the section th1 = 0, a point (th2, p2) is reachable at energy E exactly when p2^2 <= 2 m2 l2^2 (E - V(th2)), V(th2) = m2 g l2 (1 - cos th2); p1 is the root with the inner arm moving right. Crossings are located by linear interpolation within a step. The step is 4x the animation's: over 300 crossings the energy error stays below ~2e-5, the regular/chaotic classification is unchanged, and regular orbits move by ~1e-4. Each orbit's exponent is the shadow method's mean over its run (regular orbits ~0.01/s or less).
2319 - Integrator: 2-stage Gauss-Legendre (order 4, symplectic), fixed step h chosen per parameter set by a short calibration run so the relative energy error stays below 1e-6 up to the top of the energy slider (twice the both-inverted energy); 3.13 ms for the defaults, fixed-point stage solve (started from the previous step's stage derivatives extrapolated in time; tolerance 1e-12, below which the energy error does not change). Angles are wrapped to (-pi, pi] after each step. The physics costs well under 1 ms of CPU per second; drawing dominates, so traces draw at most a min/max pair per pixel column.
2420
25-Audio: a Web Worker (`src/audio-worker.ts`) integrates the audio copy with substeps of at most 3.2 ms of simulated time per sample, high-passes at 20 Hz, and streams 1024-sample chunks through a MessageChannel to an AudioWorklet (`src/audio-worklet.ts`) that plays them from a ring buffer, keeping about 0.25 s queued. The page shows how many times faster than real time the worker computes, and any dropouts. The worker also sends each chunk to the page, which feeds it to the audio spectrogram as the audio clock reaches it (2048-sample window, new column every 512 samples). The worker runs the same Lyapunov shadow on the note and sends one exponent and one energy value per 256 samples with each chunk; the page plots them in step with the spectrogram. The whole-note spectrogram keeps every column (pairs are averaged past 4096 columns) and draws only new pixel columns unless the axis or color scale changes.
21+Audio: a Web Worker (`audio-worker.ts`) integrates the audio copy with substeps of at most 3.2 ms of simulated time per sample, high-passes at 20 Hz, and streams 1024-sample chunks through a MessageChannel to an AudioWorklet (`audio-worklet.ts`) that plays them from a ring buffer, keeping about 0.25 s queued. The page shows how many times faster than real time the worker computes, and any dropouts. The worker also sends each chunk to the page, which feeds it to the audio spectrogram as the audio clock reaches it (2048-sample window, new column every 512 samples). The worker runs the same Lyapunov shadow on the note and sends one exponent and one energy value per 256 samples with each chunk; the page plots them in step with the spectrogram. The whole-note spectrogram keeps every column (pairs are averaged past 4096 columns) and draws only new pixel columns unless the axis or color scale changes.
2622
27-Code: `src/pendulum.ts` (physics), `src/sim.ts` (running simulation with energy target), `src/audio.ts` (audio setup), `src/signals.ts` (audible quantities), `src/lyapunov.ts` (Lyapunov exponent), `src/scatter.ts` (lambda vs energy), `src/cursor.ts` (shared hover/pin cursor), `src/poincare-core.ts`, `src/poincare-worker.ts`, `src/poincare.ts` (Poincare section), `src/atlas.ts` (cached sections for following the sound), `src/audio-shared.ts` (constants shared with the worker), `src/traces.ts` (strip charts), `src/spectrogram.ts` (whole-note spectrogram), `src/main.ts` (page).
23+Code: `pendulum.ts` (physics), `sim.ts` (running simulation with energy target), `audio.ts` (audio setup), `signals.ts` (audible quantities), `lyapunov.ts` (Lyapunov exponent), `scatter.ts` (lambda vs energy), `cursor.ts` (shared hover/pin cursor), `poincare-core.ts`, `poincare-worker.ts`, `poincare.ts` (Poincare section), `atlas.ts` (cached sections for following the sound), `audio-shared.ts` (constants shared with the worker), `traces.ts` (strip charts), `spectrogram.ts` (whole-note spectrogram), `main.ts` (page).
src/double-pendulum/index.htmlmodified+1−1View file
@@ -274,6 +274,6 @@
274274 <p>A map of the motions at one energy, without damping. Each time the inner arm swings down through vertical moving right, the outer arm's angle θ2 and momentum p2 are plotted as a dot. Regular motions trace closed curves; chaotic ones scatter over a region. The outline bounds what is reachable at this energy.</p>
275275 <p>The pendulum's own crossings are dark rings. Follow makes the section's energy track the pendulum (real time) or the playing note (sped up). Hover to pick out an orbit; click to start a new one there, which with Click to strike also strikes the pendulum from that point.</p>
276276 </template>
277- <script type="module" src="/src/main.ts"></script>
277+ <script type="module" src="./main.ts"></script>
278278 </body>
279279 </html>
src/double-pendulum/package-lock.jsondeleted+0−1202View file
@@ -1,1202 +0,0 @@
1-{
2- "name": "double-pendulum-chaos",
3- "lockfileVersion": 3,
4- "requires": true,
5- "packages": {
6- "": {
7- "name": "double-pendulum-chaos",
8- "devDependencies": {
9- "typescript": "^7.0.2",
10- "vite": "^8.3.4"
11- }
12- },
13- "node_modules/@oxc-project/types": {
14- "version": "0.153.0",
15- "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.153.0.tgz",
16- "integrity": "sha512-jSNrRIyvPVLL5E1z/tS5zLAprosGz/FuI1PWwEOz59j3E4TTTzSpqKb/RDGqJhhW0QHYISGXxq582wGx6T11Tw==",
17- "dev": true,
18- "license": "MIT",
19- "funding": {
20- "url": "https://github.com/sponsors/oxc-project"
21- }
22- },
23- "node_modules/@rolldown/binding-android-arm-eabi": {
24- "version": "1.2.13",
25- "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.13.tgz",
26- "integrity": "sha512-Vu/NICrgdRvUOeaJZIyB2FP63s6fO/4ud5l8dXmxDx9yDyn+mN0y+gsvsVviSli+T361Aqo0h46EuMoobrwawA==",
27- "cpu": [
28- "arm"
29- ],
30- "dev": true,
31- "license": "MIT",
32- "optional": true,
33- "os": [
34- "android"
35- ],
36- "engines": {
37- "node": "^20.19.0 || >=22.12.0"
38- }
39- },
40- "node_modules/@rolldown/binding-android-arm64": {
41- "version": "1.2.13",
42- "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.13.tgz",
43- "integrity": "sha512-6LEoIrGy5vvCd2boNnv2soPSUZj/PRvhp8R/t9KlX+mIZxThIE/ZqxrGXxmuNX0FuvQFvLdZr7S4HrYpYBVhEQ==",
44- "cpu": [
45- "arm64"
46- ],
47- "dev": true,
48- "license": "MIT",
49- "optional": true,
50- "os": [
51- "android"
52- ],
53- "engines": {
54- "node": "^20.19.0 || >=22.12.0"
55- }
56- },
57- "node_modules/@rolldown/binding-darwin-arm64": {
58- "version": "1.2.13",
59- "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.13.tgz",
60- "integrity": "sha512-CaJSRzlQ6Q+zkDMVA9OkVxBjVoPRCDEE3v0FSJ9kIQBgRjFApA3Si4ijhbNVUji7ZJt+PfO0e46qy4nPInHUPA==",
61- "cpu": [
62- "arm64"
63- ],
64- "dev": true,
65- "license": "MIT",
66- "optional": true,
67- "os": [
68- "darwin"
69- ],
70- "engines": {
71- "node": "^20.19.0 || >=22.12.0"
72- }
73- },
74- "node_modules/@rolldown/binding-darwin-x64": {
75- "version": "1.2.13",
76- "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.13.tgz",
77- "integrity": "sha512-e4r340I5hxSzJkD3YJBxgzq/rJ3wwxwdLLb2FPt1Zpyem20nrQPT11DDEEkazwxaHumH8wpglsp/Ozgj/wT6mw==",
78- "cpu": [
79- "x64"
80- ],
81- "dev": true,
82- "license": "MIT",
83- "optional": true,
84- "os": [
85- "darwin"
86- ],
87- "engines": {
88- "node": "^20.19.0 || >=22.12.0"
89- }
90- },
91- "node_modules/@rolldown/binding-freebsd-x64": {
92- "version": "1.2.13",
93- "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.13.tgz",
94- "integrity": "sha512-hoBWDZ4/K/mJKVMbsgUd1jfpA2DiOrJ54mpegObU9TBHBBrtX4FLIXJJQRDPZ/2Dn91GvPob+Z2hwKJzf4E5SQ==",
95- "cpu": [
96- "x64"
97- ],
98- "dev": true,
99- "license": "MIT",
100- "optional": true,
101- "os": [
102- "freebsd"
103- ],
104- "engines": {
105- "node": "^20.19.0 || >=22.12.0"
106- }
107- },
108- "node_modules/@rolldown/binding-linux-arm-gnueabihf": {
109- "version": "1.2.13",
110- "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.13.tgz",
111- "integrity": "sha512-oF9fZu3vTWnTiVAkDFxU9PJLt1nh20K9RRpuHsR7ZnE2L7XookBbDBVLojCVSLGbvtSlxeUNnjAj6DbNsKyzbg==",
112- "cpu": [
113- "arm"
114- ],
115- "dev": true,
116- "license": "MIT",
117- "optional": true,
118- "os": [
119- "linux"
120- ],
121- "engines": {
122- "node": "^20.19.0 || >=22.12.0"
123- }
124- },
125- "node_modules/@rolldown/binding-linux-arm64-gnu": {
126- "version": "1.2.13",
127- "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.13.tgz",
128- "integrity": "sha512-DTpb/+jMiqWMPgphn4ksW1zW+XLJ6dJJ9FMvnIk9I291LhCctMPhMYJrP5t9p6C9qa3KXadDdUEZHBaPh3kNxQ==",
129- "cpu": [
130- "arm64"
131- ],
132- "dev": true,
133- "libc": [
134- "glibc"
135- ],
136- "license": "MIT",
137- "optional": true,
138- "os": [
139- "linux"
140- ],
141- "engines": {
142- "node": "^20.19.0 || >=22.12.0"
143- }
144- },
145- "node_modules/@rolldown/binding-linux-arm64-musl": {
146- "version": "1.2.13",
147- "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.13.tgz",
148- "integrity": "sha512-xU+Kvv9N2A3SY35zFh1FWWNjxbadE/BKeqnkXe7vScU4GZsw757BlVmDLITZEq8DPpk6m7uoDYkK7P+x/uU5Ow==",
149- "cpu": [
150- "arm64"
151- ],
152- "dev": true,
153- "libc": [
154- "musl"
155- ],
156- "license": "MIT",
157- "optional": true,
158- "os": [
159- "linux"
160- ],
161- "engines": {
162- "node": "^20.19.0 || >=22.12.0"
163- }
164- },
165- "node_modules/@rolldown/binding-linux-ppc64-gnu": {
166- "version": "1.2.13",
167- "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.13.tgz",
168- "integrity": "sha512-XMk/hnhh25oQ756Lclb1Q11h4ll0xC0Esa5w4Vzp+mAE2S3AGJgeM0SonrIgY8fHhjarb6hcoTuwoFG0VVP2ig==",
169- "cpu": [
170- "ppc64"
171- ],
172- "dev": true,
173- "libc": [
174- "glibc"
175- ],
176- "license": "MIT",
177- "optional": true,
178- "os": [
179- "linux"
180- ],
181- "engines": {
182- "node": "^20.19.0 || >=22.12.0"
183- }
184- },
185- "node_modules/@rolldown/binding-linux-s390x-gnu": {
186- "version": "1.2.13",
187- "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.13.tgz",
188- "integrity": "sha512-U1BoTHYmlywE2n3HUWjzZ+8EQsKr+OwpxYuDWaubx42tUL8QSL7nOqRz/NVHanibbFP3M1Rxuprz/wNcbMKjqw==",
189- "cpu": [
190- "s390x"
191- ],
192- "dev": true,
193- "libc": [
194- "glibc"
195- ],
196- "license": "MIT",
197- "optional": true,
198- "os": [
199- "linux"
200- ],
201- "engines": {
202- "node": "^20.19.0 || >=22.12.0"
203- }
204- },
205- "node_modules/@rolldown/binding-linux-x64-gnu": {
206- "version": "1.2.13",
207- "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.13.tgz",
208- "integrity": "sha512-J4cm80Pc6wEwV5AZ42o62sIQgDk+Y7R2s646jIwuvO+4V46QB58N4vVuMPWCBaNgursXLpmUWJV//WQAkPvHBQ==",
209- "cpu": [
210- "x64"
211- ],
212- "dev": true,
213- "libc": [
214- "glibc"
215- ],
216- "license": "MIT",
217- "optional": true,
218- "os": [
219- "linux"
220- ],
221- "engines": {
222- "node": "^20.19.0 || >=22.12.0"
223- }
224- },
225- "node_modules/@rolldown/binding-linux-x64-musl": {
226- "version": "1.2.13",
227- "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.13.tgz",
228- "integrity": "sha512-W0euZS40U4V6JkmEnVCIX9mcsL6cbXQ4X8c2w+MMOIjFg68TXUW+u3RT+2e1t1pv9mg/EP3pDtK3POXdQ7Rxyw==",
229- "cpu": [
230- "x64"
231- ],
232- "dev": true,
233- "libc": [
234- "musl"
235- ],
236- "license": "MIT",
237- "optional": true,
238- "os": [
239- "linux"
240- ],
241- "engines": {
242- "node": "^20.19.0 || >=22.12.0"
243- }
244- },
245- "node_modules/@rolldown/binding-openharmony-arm64": {
246- "version": "1.2.13",
247- "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.13.tgz",
248- "integrity": "sha512-do/AF/4n9MRshia3a5aXoy8xM2c6mgfT6D7H/dY6my5z+hfCIR4ab7mk4IRj++nsEQpKy39kd0DQnDq+r692tw==",
249- "cpu": [
250- "arm64"
251- ],
252- "dev": true,
253- "license": "MIT",
254- "optional": true,
255- "os": [
256- "openharmony"
257- ],
258- "engines": {
259- "node": "^20.19.0 || >=22.12.0"
260- }
261- },
262- "node_modules/@rolldown/binding-win32-arm64-msvc": {
263- "version": "1.2.13",
264- "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.13.tgz",
265- "integrity": "sha512-6FtUf2cJZobutJdxnGYX8z0vtbgqU0b2v8vfBhkXb9wp2MppMFtkunE+IaOntyi01rkIQoPW2GHABtfzsC0BRQ==",
266- "cpu": [
267- "arm64"
268- ],
269- "dev": true,
270- "license": "MIT",
271- "optional": true,
272- "os": [
273- "win32"
274- ],
275- "engines": {
276- "node": "^20.19.0 || >=22.12.0"
277- }
278- },
279- "node_modules/@rolldown/binding-win32-x64-msvc": {
280- "version": "1.2.13",
281- "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.13.tgz",
282- "integrity": "sha512-UKhFaZTeTcbKrb3iU82m17jVcPEAoqDkCN5MBqgPyZSwp/cQmib21crTS0DdH7T1B7wZRWEI89YQAOJMet5yhg==",
283- "cpu": [
284- "x64"
285- ],
286- "dev": true,
287- "license": "MIT",
288- "optional": true,
289- "os": [
290- "win32"
291- ],
292- "engines": {
293- "node": "^20.19.0 || >=22.12.0"
294- }
295- },
296- "node_modules/@rolldown/pluginutils": {
297- "version": "1.0.1",
298- "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz",
299- "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==",
300- "dev": true,
301- "license": "MIT"
302- },
303- "node_modules/@typescript/typescript-aix-ppc64": {
304- "version": "7.0.2",
305- "resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.0.2.tgz",
306- "integrity": "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==",
307- "cpu": [
308- "ppc64"
309- ],
310- "dev": true,
311- "license": "Apache-2.0",
312- "optional": true,
313- "os": [
314- "aix"
315- ],
316- "engines": {
317- "node": ">=16.20.0"
318- }
319- },
320- "node_modules/@typescript/typescript-darwin-arm64": {
321- "version": "7.0.2",
322- "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.0.2.tgz",
323- "integrity": "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==",
324- "cpu": [
325- "arm64"
326- ],
327- "dev": true,
328- "license": "Apache-2.0",
329- "optional": true,
330- "os": [
331- "darwin"
332- ],
333- "engines": {
334- "node": ">=16.20.0"
335- }
336- },
337- "node_modules/@typescript/typescript-darwin-x64": {
338- "version": "7.0.2",
339- "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.0.2.tgz",
340- "integrity": "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==",
341- "cpu": [
342- "x64"
343- ],
344- "dev": true,
345- "license": "Apache-2.0",
346- "optional": true,
347- "os": [
348- "darwin"
349- ],
350- "engines": {
351- "node": ">=16.20.0"
352- }
353- },
354- "node_modules/@typescript/typescript-freebsd-arm64": {
355- "version": "7.0.2",
356- "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.0.2.tgz",
357- "integrity": "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==",
358- "cpu": [
359- "arm64"
360- ],
361- "dev": true,
362- "license": "Apache-2.0",
363- "optional": true,
364- "os": [
365- "freebsd"
366- ],
367- "engines": {
368- "node": ">=16.20.0"
369- }
370- },
371- "node_modules/@typescript/typescript-freebsd-x64": {
372- "version": "7.0.2",
373- "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.0.2.tgz",
374- "integrity": "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==",
375- "cpu": [
376- "x64"
377- ],
378- "dev": true,
379- "license": "Apache-2.0",
380- "optional": true,
381- "os": [
382- "freebsd"
383- ],
384- "engines": {
385- "node": ">=16.20.0"
386- }
387- },
388- "node_modules/@typescript/typescript-linux-arm": {
389- "version": "7.0.2",
390- "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.0.2.tgz",
391- "integrity": "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==",
392- "cpu": [
393- "arm"
394- ],
395- "dev": true,
396- "license": "Apache-2.0",
397- "optional": true,
398- "os": [
399- "linux"
400- ],
401- "engines": {
402- "node": ">=16.20.0"
403- }
404- },
405- "node_modules/@typescript/typescript-linux-arm64": {
406- "version": "7.0.2",
407- "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.0.2.tgz",
408- "integrity": "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==",
409- "cpu": [
410- "arm64"
411- ],
412- "dev": true,
413- "license": "Apache-2.0",
414- "optional": true,
415- "os": [
416- "linux"
417- ],
418- "engines": {
419- "node": ">=16.20.0"
420- }
421- },
422- "node_modules/@typescript/typescript-linux-loong64": {
423- "version": "7.0.2",
424- "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.0.2.tgz",
425- "integrity": "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==",
426- "cpu": [
427- "loong64"
428- ],
429- "dev": true,
430- "license": "Apache-2.0",
431- "optional": true,
432- "os": [
433- "linux"
434- ],
435- "engines": {
436- "node": ">=16.20.0"
437- }
438- },
439- "node_modules/@typescript/typescript-linux-mips64el": {
440- "version": "7.0.2",
441- "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.0.2.tgz",
442- "integrity": "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==",
443- "cpu": [
444- "mips64el"
445- ],
446- "dev": true,
447- "license": "Apache-2.0",
448- "optional": true,
449- "os": [
450- "linux"
451- ],
452- "engines": {
453- "node": ">=16.20.0"
454- }
455- },
456- "node_modules/@typescript/typescript-linux-ppc64": {
457- "version": "7.0.2",
458- "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.0.2.tgz",
459- "integrity": "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==",
460- "cpu": [
461- "ppc64"
462- ],
463- "dev": true,
464- "license": "Apache-2.0",
465- "optional": true,
466- "os": [
467- "linux"
468- ],
469- "engines": {
470- "node": ">=16.20.0"
471- }
472- },
473- "node_modules/@typescript/typescript-linux-riscv64": {
474- "version": "7.0.2",
475- "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.0.2.tgz",
476- "integrity": "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==",
477- "cpu": [
478- "riscv64"
479- ],
480- "dev": true,
481- "license": "Apache-2.0",
482- "optional": true,
483- "os": [
484- "linux"
485- ],
486- "engines": {
487- "node": ">=16.20.0"
488- }
489- },
490- "node_modules/@typescript/typescript-linux-s390x": {
491- "version": "7.0.2",
492- "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.0.2.tgz",
493- "integrity": "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==",
494- "cpu": [
495- "s390x"
496- ],
497- "dev": true,
498- "license": "Apache-2.0",
499- "optional": true,
500- "os": [
501- "linux"
502- ],
503- "engines": {
504- "node": ">=16.20.0"
505- }
506- },
507- "node_modules/@typescript/typescript-linux-x64": {
508- "version": "7.0.2",
509- "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.0.2.tgz",
510- "integrity": "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==",
511- "cpu": [
512- "x64"
513- ],
514- "dev": true,
515- "license": "Apache-2.0",
516- "optional": true,
517- "os": [
518- "linux"
519- ],
520- "engines": {
521- "node": ">=16.20.0"
522- }
523- },
524- "node_modules/@typescript/typescript-netbsd-arm64": {
525- "version": "7.0.2",
526- "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.0.2.tgz",
527- "integrity": "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==",
528- "cpu": [
529- "arm64"
530- ],
531- "dev": true,
532- "license": "Apache-2.0",
533- "optional": true,
534- "os": [
535- "netbsd"
536- ],
537- "engines": {
538- "node": ">=16.20.0"
539- }
540- },
541- "node_modules/@typescript/typescript-netbsd-x64": {
542- "version": "7.0.2",
543- "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.0.2.tgz",
544- "integrity": "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==",
545- "cpu": [
546- "x64"
547- ],
548- "dev": true,
549- "license": "Apache-2.0",
550- "optional": true,
551- "os": [
552- "netbsd"
553- ],
554- "engines": {
555- "node": ">=16.20.0"
556- }
557- },
558- "node_modules/@typescript/typescript-openbsd-arm64": {
559- "version": "7.0.2",
560- "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.0.2.tgz",
561- "integrity": "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==",
562- "cpu": [
563- "arm64"
564- ],
565- "dev": true,
566- "license": "Apache-2.0",
567- "optional": true,
568- "os": [
569- "openbsd"
570- ],
571- "engines": {
572- "node": ">=16.20.0"
573- }
574- },
575- "node_modules/@typescript/typescript-openbsd-x64": {
576- "version": "7.0.2",
577- "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.0.2.tgz",
578- "integrity": "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==",
579- "cpu": [
580- "x64"
581- ],
582- "dev": true,
583- "license": "Apache-2.0",
584- "optional": true,
585- "os": [
586- "openbsd"
587- ],
588- "engines": {
589- "node": ">=16.20.0"
590- }
591- },
592- "node_modules/@typescript/typescript-sunos-x64": {
593- "version": "7.0.2",
594- "resolved": "https://registry.npmjs.org/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.0.2.tgz",
595- "integrity": "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==",
596- "cpu": [
597- "x64"
598- ],
599- "dev": true,
600- "license": "Apache-2.0",
601- "optional": true,
602- "os": [
603- "sunos"
604- ],
605- "engines": {
606- "node": ">=16.20.0"
607- }
608- },
609- "node_modules/@typescript/typescript-win32-arm64": {
610- "version": "7.0.2",
611- "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.0.2.tgz",
612- "integrity": "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==",
613- "cpu": [
614- "arm64"
615- ],
616- "dev": true,
617- "license": "Apache-2.0",
618- "optional": true,
619- "os": [
620- "win32"
621- ],
622- "engines": {
623- "node": ">=16.20.0"
624- }
625- },
626- "node_modules/@typescript/typescript-win32-x64": {
627- "version": "7.0.2",
628- "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.0.2.tgz",
629- "integrity": "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==",
630- "cpu": [
631- "x64"
632- ],
633- "dev": true,
634- "license": "Apache-2.0",
635- "optional": true,
636- "os": [
637- "win32"
638- ],
639- "engines": {
640- "node": ">=16.20.0"
641- }
642- },
643- "node_modules/detect-libc": {
644- "version": "2.1.2",
645- "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
646- "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
647- "dev": true,
648- "license": "Apache-2.0",
649- "engines": {
650- "node": ">=8"
651- }
652- },
653- "node_modules/fdir": {
654- "version": "6.5.0",
655- "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
656- "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
657- "dev": true,
658- "license": "MIT",
659- "engines": {
660- "node": ">=12.0.0"
661- },
662- "peerDependencies": {
663- "picomatch": "^3 || ^4"
664- },
665- "peerDependenciesMeta": {
666- "picomatch": {
667- "optional": true
668- }
669- }
670- },
671- "node_modules/fsevents": {
672- "version": "2.3.3",
673- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
674- "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
675- "dev": true,
676- "hasInstallScript": true,
677- "license": "MIT",
678- "optional": true,
679- "os": [
680- "darwin"
681- ],
682- "engines": {
683- "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
684- }
685- },
686- "node_modules/lightningcss": {
687- "version": "1.33.0",
688- "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz",
689- "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==",
690- "dev": true,
691- "license": "MPL-2.0",
692- "dependencies": {
693- "detect-libc": "^2.0.3"
694- },
695- "engines": {
696- "node": ">= 12.0.0"
697- },
698- "funding": {
699- "type": "opencollective",
700- "url": "https://opencollective.com/parcel"
701- },
702- "optionalDependencies": {
703- "lightningcss-android-arm64": "1.33.0",
704- "lightningcss-darwin-arm64": "1.33.0",
705- "lightningcss-darwin-x64": "1.33.0",
706- "lightningcss-freebsd-x64": "1.33.0",
707- "lightningcss-linux-arm-gnueabihf": "1.33.0",
708- "lightningcss-linux-arm64-gnu": "1.33.0",
709- "lightningcss-linux-arm64-musl": "1.33.0",
710- "lightningcss-linux-x64-gnu": "1.33.0",
711- "lightningcss-linux-x64-musl": "1.33.0",
712- "lightningcss-win32-arm64-msvc": "1.33.0",
713- "lightningcss-win32-x64-msvc": "1.33.0"
714- }
715- },
716- "node_modules/lightningcss-android-arm64": {
717- "version": "1.33.0",
718- "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz",
719- "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==",
720- "cpu": [
721- "arm64"
722- ],
723- "dev": true,
724- "license": "MPL-2.0",
725- "optional": true,
726- "os": [
727- "android"
728- ],
729- "engines": {
730- "node": ">= 12.0.0"
731- },
732- "funding": {
733- "type": "opencollective",
734- "url": "https://opencollective.com/parcel"
735- }
736- },
737- "node_modules/lightningcss-darwin-arm64": {
738- "version": "1.33.0",
739- "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz",
740- "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==",
741- "cpu": [
742- "arm64"
743- ],
744- "dev": true,
745- "license": "MPL-2.0",
746- "optional": true,
747- "os": [
748- "darwin"
749- ],
750- "engines": {
751- "node": ">= 12.0.0"
752- },
753- "funding": {
754- "type": "opencollective",
755- "url": "https://opencollective.com/parcel"
756- }
757- },
758- "node_modules/lightningcss-darwin-x64": {
759- "version": "1.33.0",
760- "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz",
761- "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==",
762- "cpu": [
763- "x64"
764- ],
765- "dev": true,
766- "license": "MPL-2.0",
767- "optional": true,
768- "os": [
769- "darwin"
770- ],
771- "engines": {
772- "node": ">= 12.0.0"
773- },
774- "funding": {
775- "type": "opencollective",
776- "url": "https://opencollective.com/parcel"
777- }
778- },
779- "node_modules/lightningcss-freebsd-x64": {
780- "version": "1.33.0",
781- "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz",
782- "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==",
783- "cpu": [
784- "x64"
785- ],
786- "dev": true,
787- "license": "MPL-2.0",
788- "optional": true,
789- "os": [
790- "freebsd"
791- ],
792- "engines": {
793- "node": ">= 12.0.0"
794- },
795- "funding": {
796- "type": "opencollective",
797- "url": "https://opencollective.com/parcel"
798- }
799- },
800- "node_modules/lightningcss-linux-arm-gnueabihf": {
801- "version": "1.33.0",
802- "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz",
803- "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==",
804- "cpu": [
805- "arm"
806- ],
807- "dev": true,
808- "license": "MPL-2.0",
809- "optional": true,
810- "os": [
811- "linux"
812- ],
813- "engines": {
814- "node": ">= 12.0.0"
815- },
816- "funding": {
817- "type": "opencollective",
818- "url": "https://opencollective.com/parcel"
819- }
820- },
821- "node_modules/lightningcss-linux-arm64-gnu": {
822- "version": "1.33.0",
823- "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz",
824- "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==",
825- "cpu": [
826- "arm64"
827- ],
828- "dev": true,
829- "libc": [
830- "glibc"
831- ],
832- "license": "MPL-2.0",
833- "optional": true,
834- "os": [
835- "linux"
836- ],
837- "engines": {
838- "node": ">= 12.0.0"
839- },
840- "funding": {
841- "type": "opencollective",
842- "url": "https://opencollective.com/parcel"
843- }
844- },
845- "node_modules/lightningcss-linux-arm64-musl": {
846- "version": "1.33.0",
847- "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz",
848- "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==",
849- "cpu": [
850- "arm64"
851- ],
852- "dev": true,
853- "libc": [
854- "musl"
855- ],
856- "license": "MPL-2.0",
857- "optional": true,
858- "os": [
859- "linux"
860- ],
861- "engines": {
862- "node": ">= 12.0.0"
863- },
864- "funding": {
865- "type": "opencollective",
866- "url": "https://opencollective.com/parcel"
867- }
868- },
869- "node_modules/lightningcss-linux-x64-gnu": {
870- "version": "1.33.0",
871- "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz",
872- "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==",
873- "cpu": [
874- "x64"
875- ],
876- "dev": true,
877- "libc": [
878- "glibc"
879- ],
880- "license": "MPL-2.0",
881- "optional": true,
882- "os": [
883- "linux"
884- ],
885- "engines": {
886- "node": ">= 12.0.0"
887- },
888- "funding": {
889- "type": "opencollective",
890- "url": "https://opencollective.com/parcel"
891- }
892- },
893- "node_modules/lightningcss-linux-x64-musl": {
894- "version": "1.33.0",
895- "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz",
896- "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==",
897- "cpu": [
898- "x64"
899- ],
900- "dev": true,
901- "libc": [
902- "musl"
903- ],
904- "license": "MPL-2.0",
905- "optional": true,
906- "os": [
907- "linux"
908- ],
909- "engines": {
910- "node": ">= 12.0.0"
911- },
912- "funding": {
913- "type": "opencollective",
914- "url": "https://opencollective.com/parcel"
915- }
916- },
917- "node_modules/lightningcss-win32-arm64-msvc": {
918- "version": "1.33.0",
919- "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz",
920- "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==",
921- "cpu": [
922- "arm64"
923- ],
924- "dev": true,
925- "license": "MPL-2.0",
926- "optional": true,
927- "os": [
928- "win32"
929- ],
930- "engines": {
931- "node": ">= 12.0.0"
932- },
933- "funding": {
934- "type": "opencollective",
935- "url": "https://opencollective.com/parcel"
936- }
937- },
938- "node_modules/lightningcss-win32-x64-msvc": {
939- "version": "1.33.0",
940- "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz",
941- "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==",
942- "cpu": [
943- "x64"
944- ],
945- "dev": true,
946- "license": "MPL-2.0",
947- "optional": true,
948- "os": [
949- "win32"
950- ],
951- "engines": {
952- "node": ">= 12.0.0"
953- },
954- "funding": {
955- "type": "opencollective",
956- "url": "https://opencollective.com/parcel"
957- }
958- },
959- "node_modules/nanoid": {
960- "version": "3.3.20",
961- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz",
962- "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==",
963- "dev": true,
964- "funding": [
965- {
966- "type": "github",
967- "url": "https://github.com/sponsors/ai"
968- }
969- ],
970- "license": "MIT",
971- "bin": {
972- "nanoid": "bin/nanoid.cjs"
973- },
974- "engines": {
975- "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
976- }
977- },
978- "node_modules/picocolors": {
979- "version": "1.1.1",
980- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
981- "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
982- "dev": true,
983- "license": "ISC"
984- },
985- "node_modules/picomatch": {
986- "version": "4.0.7",
987- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz",
988- "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==",
989- "dev": true,
990- "license": "MIT",
991- "engines": {
992- "node": ">=12"
993- },
994- "funding": {
995- "url": "https://github.com/sponsors/jonschlinkert"
996- }
997- },
998- "node_modules/postcss": {
999- "version": "8.5.29",
1000- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.29.tgz",
1001- "integrity": "sha512-49cGhUbXj8Qenv0iTMxA1cFBzxXoctpC9Ujd77t1WcbJIr6nF/eI7g/8MgxrYldFRuAXvja7xQRwavoW7kgrxQ==",
1002- "dev": true,
1003- "funding": [
1004- {
1005- "type": "opencollective",
1006- "url": "https://opencollective.com/postcss/"
1007- },
1008- {
1009- "type": "tidelift",
1010- "url": "https://tidelift.com/funding/github/npm/postcss"
1011- },
1012- {
1013- "type": "github",
1014- "url": "https://github.com/sponsors/ai"
1015- }
1016- ],
1017- "license": "MIT",
1018- "dependencies": {
1019- "nanoid": "^3.3.19",
1020- "picocolors": "^1.1.1",
1021- "source-map-js": "^1.2.2"
1022- },
1023- "engines": {
1024- "node": "^10 || ^12 || >=14"
1025- }
1026- },
1027- "node_modules/rolldown": {
1028- "version": "1.2.13",
1029- "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.13.tgz",
1030- "integrity": "sha512-/5apOkwWxtACcQtZ5WlrAsKS4JwXVThQPJZ3RqIruRY9UG9M8e0VlXN068xum/JRKA4aIUoJ13ghe/kGCvidiQ==",
1031- "dev": true,
1032- "license": "MIT",
1033- "dependencies": {
1034- "@oxc-project/types": "=0.153.0",
1035- "@rolldown/pluginutils": "^1.0.0"
1036- },
1037- "bin": {
1038- "rolldown": "bin/cli.mjs"
1039- },
1040- "engines": {
1041- "node": "^20.19.0 || >=22.12.0"
1042- },
1043- "optionalDependencies": {
1044- "@rolldown/binding-android-arm-eabi": "1.2.13",
1045- "@rolldown/binding-android-arm64": "1.2.13",
1046- "@rolldown/binding-darwin-arm64": "1.2.13",
1047- "@rolldown/binding-darwin-x64": "1.2.13",
1048- "@rolldown/binding-freebsd-x64": "1.2.13",
1049- "@rolldown/binding-linux-arm-gnueabihf": "1.2.13",
1050- "@rolldown/binding-linux-arm64-gnu": "1.2.13",
1051- "@rolldown/binding-linux-arm64-musl": "1.2.13",
1052- "@rolldown/binding-linux-ppc64-gnu": "1.2.13",
1053- "@rolldown/binding-linux-s390x-gnu": "1.2.13",
1054- "@rolldown/binding-linux-x64-gnu": "1.2.13",
1055- "@rolldown/binding-linux-x64-musl": "1.2.13",
1056- "@rolldown/binding-openharmony-arm64": "1.2.13",
1057- "@rolldown/binding-win32-arm64-msvc": "1.2.13",
1058- "@rolldown/binding-win32-x64-msvc": "1.2.13"
1059- }
1060- },
1061- "node_modules/source-map-js": {
1062- "version": "1.2.2",
1063- "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz",
1064- "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==",
1065- "dev": true,
1066- "license": "BSD-3-Clause",
1067- "engines": {
1068- "node": ">=0.10.0"
1069- }
1070- },
1071- "node_modules/tinyglobby": {
1072- "version": "0.2.17",
1073- "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
1074- "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
1075- "dev": true,
1076- "license": "MIT",
1077- "dependencies": {
1078- "fdir": "^6.5.0",
1079- "picomatch": "^4.0.4"
1080- },
1081- "engines": {
1082- "node": ">=12.0.0"
1083- },
1084- "funding": {
1085- "url": "https://github.com/sponsors/SuperchupuDev"
1086- }
1087- },
1088- "node_modules/typescript": {
1089- "version": "7.0.2",
1090- "resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz",
1091- "integrity": "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==",
1092- "dev": true,
1093- "license": "Apache-2.0",
1094- "bin": {
1095- "tsc": "bin/tsc"
1096- },
1097- "engines": {
1098- "node": ">=16.20.0"
1099- },
1100- "optionalDependencies": {
1101- "@typescript/typescript-aix-ppc64": "7.0.2",
1102- "@typescript/typescript-darwin-arm64": "7.0.2",
1103- "@typescript/typescript-darwin-x64": "7.0.2",
1104- "@typescript/typescript-freebsd-arm64": "7.0.2",
1105- "@typescript/typescript-freebsd-x64": "7.0.2",
1106- "@typescript/typescript-linux-arm": "7.0.2",
1107- "@typescript/typescript-linux-arm64": "7.0.2",
1108- "@typescript/typescript-linux-loong64": "7.0.2",
1109- "@typescript/typescript-linux-mips64el": "7.0.2",
1110- "@typescript/typescript-linux-ppc64": "7.0.2",
1111- "@typescript/typescript-linux-riscv64": "7.0.2",
1112- "@typescript/typescript-linux-s390x": "7.0.2",
1113- "@typescript/typescript-linux-x64": "7.0.2",
1114- "@typescript/typescript-netbsd-arm64": "7.0.2",
1115- "@typescript/typescript-netbsd-x64": "7.0.2",
1116- "@typescript/typescript-openbsd-arm64": "7.0.2",
1117- "@typescript/typescript-openbsd-x64": "7.0.2",
1118- "@typescript/typescript-sunos-x64": "7.0.2",
1119- "@typescript/typescript-win32-arm64": "7.0.2",
1120- "@typescript/typescript-win32-x64": "7.0.2"
1121- }
1122- },
1123- "node_modules/vite": {
1124- "version": "8.3.4",
1125- "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.4.tgz",
1126- "integrity": "sha512-IftLE8phJhdNRIGNEV4JCCvfNScoZQVfcXaN8TQropWGJtOHcu+iAB9pHcuGvmEb/kddw0LBe/jS8ztdC6xdiQ==",
1127- "dev": true,
1128- "license": "MIT",
1129- "dependencies": {
1130- "lightningcss": "^1.33.0",
1131- "picomatch": "^4.0.7",
1132- "postcss": "^8.5.29",
1133- "rolldown": "~1.2.12",
1134- "tinyglobby": "^0.2.17"
1135- },
1136- "bin": {
1137- "vite": "bin/vite.js"
1138- },
1139- "engines": {
1140- "node": "^20.19.0 || >=22.12.0"
1141- },
1142- "funding": {
1143- "url": "https://github.com/vitejs/vite?sponsor=1"
1144- },
1145- "optionalDependencies": {
1146- "fsevents": "~2.3.3"
1147- },
1148- "peerDependencies": {
1149- "@types/node": "^20.19.0 || >=22.12.0",
1150- "@vitejs/devtools": "^0.7.1",
1151- "esbuild": "^0.27.0 || ^0.28.0",
1152- "jiti": ">=1.21.0",
1153- "less": "^4.0.0",
1154- "sass": "^1.70.0",
1155- "sass-embedded": "^1.70.0",
1156- "stylus": ">=0.54.8",
1157- "sugarss": "^5.0.0",
1158- "terser": "^5.16.0",
1159- "tsx": "^4.8.1",
1160- "yaml": "^2.4.2"
1161- },
1162- "peerDependenciesMeta": {
1163- "@types/node": {
1164- "optional": true
1165- },
1166- "@vitejs/devtools": {
1167- "optional": true
1168- },
1169- "esbuild": {
1170- "optional": true
1171- },
1172- "jiti": {
1173- "optional": true
1174- },
1175- "less": {
1176- "optional": true
1177- },
1178- "sass": {
1179- "optional": true
1180- },
1181- "sass-embedded": {
1182- "optional": true
1183- },
1184- "stylus": {
1185- "optional": true
1186- },
1187- "sugarss": {
1188- "optional": true
1189- },
1190- "terser": {
1191- "optional": true
1192- },
1193- "tsx": {
1194- "optional": true
1195- },
1196- "yaml": {
1197- "optional": true
1198- }
1199- }
1200- }
1201- }
1202-}
src/double-pendulum/package.jsondeleted+0−14View file
@@ -1,14 +0,0 @@
1-{
2- "name": "double-pendulum-chaos",
3- "private": true,
4- "type": "module",
5- "scripts": {
6- "dev": "vite",
7- "build": "tsc && vite build",
8- "preview": "vite preview"
9- },
10- "devDependencies": {
11- "typescript": "^7.0.2",
12- "vite": "^8.3.4"
13- }
14-}
src/double-pendulum/vite.config.tsdeleted+0−8View file
@@ -1,8 +0,0 @@
1-import { defineConfig } from "vite";
2-
3-// Relative asset paths, so the build works wherever the site is served
4-// (an origin root or a /<collection>/<repo>/site/ path); ES module workers.
5-export default defineConfig({
6- base: "./",
7- worker: { format: "es" },
8-});
src/fput-chain/.github/workflows/pages.ymldeleted+0−41View file
@@ -1,41 +0,0 @@
1-name: Deploy site
2-
3-on:
4- push:
5- branches: [main]
6- workflow_dispatch:
7-
8-permissions:
9- contents: read
10- pages: write
11- id-token: write
12-
13-concurrency:
14- group: pages
15- cancel-in-progress: true
16-
17-jobs:
18- build:
19- runs-on: ubuntu-latest
20- steps:
21- - uses: actions/checkout@v4
22- - uses: actions/setup-node@v4
23- with:
24- node-version: 22
25- cache: npm
26- - run: npm ci
27- - run: npm run build
28- - uses: actions/configure-pages@v5
29- - uses: actions/upload-pages-artifact@v3
30- with:
31- path: dist
32-
33- deploy:
34- needs: build
35- runs-on: ubuntu-latest
36- environment:
37- name: github-pages
38- url: ${{ steps.deployment.outputs.page_url }}
39- steps:
40- - id: deployment
41- uses: actions/deploy-pages@v4
src/fput-chain/.gitignoredeleted+0−2View file
@@ -1,2 +0,0 @@
1-node_modules
2-dist
src/fput-chain/README.mdmodified+7−11View file
@@ -1,21 +1,17 @@
1-# fput-chain-chaos
1+# fput-chain
22
33 Struck FPUT-beta chain, step 2 of "Listening for chaos" (after double-pendulum-chaos). Work in progress.
44
5-Current state: N masses (2 to 64, default 2) between two fixed walls, joined by springs with tension k d + k3 d^3. The chain sits at rest until Strike, which gives it energy E (log slider, 10 µJ to 100 J, default 2.51 J) as velocity, either on one mass j alone (default mass 1, a hammer blow at one end) or in the shape of one normal mode n. The left card animates the chain (top: to scale, rest spacing a; below: the same displacements drawn sideways as a profile, scaled to fit, with the scale given) with the time and the relative energy error. Speed: 0.1x to 20x real time (default 1x). The time and current energy show at the top left. The Ghost chip (as in the pendulum app) adds a second chain started from the same state with every mass moved by the same amount, 10^k / sqrt(N) m (total offset 10^k m, k = -12 to -1, default -10; moving only the struck mass would put the offset along the motion itself, which does not grow), run in lockstep without renormalization and drawn in translucent orange underneath; restarted on Strike, on "Restart ghost" (from the chain's current state), when switched on or when the offset changes; a readout gives the separation (root sum of squared displacement differences, m) and the time since the ghost started, and a "Separation" chart (log, last 60 s; offered only with the ghost on) plots it. A "Lyapunov" chart (last 60 s; the first 5 s after a strike left out of its y range) shows the largest Lyapunov exponent over the last 10 s and since the strike (`src/lyapunov.ts`, two-trajectory Benettin method as in the pendulum app: a hidden shadow chain 1e-8 away, displacements in units of d0 and momenta in units of sqrt(m k) d0, renormalized every step). Checked (N = 4, mass-1 strike, 300 s): without damping 0.1 J ~0.05 to 0.1 /s (weak chaos; N = 4 onset is near 3 k d0^2 = 75 mJ), 0.5 J ~0.5 /s, 2.51 J ~0.9 /s, 25 J ~1.7 /s; with tau 540 s, 0.1 J falls to ~0.01 /s and 2.51 J falls from ~1.0 to ~0.3 /s by 300 s. The slider is labelled "Strike energy". Parameters (m, k, k3, a, tau) fold out underneath with derived values (mode frequencies, d0, nonlinear energy scale, integrator step); Apply or changing N returns the chain to rest. The "Real time" card on the right has toggle chips for its charts. "Mode history" (off by default) is a scrolling heat map of each mode's share of the harmonic energy, log10(E_n / sum E) over 6 decades (inferno colors, mode 1 at the bottom), over the last 300 s (`LiveModeHistory` in `src/modehistory.ts`); the cascade of energy up the modes shows here (e.g. N = 32, mode-1 strike at 10 mJ: the top mode's share goes from ~1e-28 to ~1e-12 in 10 periods of mode 1 and ~3e-5 in 50 without damping; with tau 540 s the damping wins and the spread stalls near mode 17). "Mode bars" shows the current harmonic energy of each normal mode relative to E on a log scale (12 decades), with the number of modes sharing the energy (inverse participation, 1 / sum (E_n / sum E)^2). The other chips are charts over time (from `src/traces.ts` and `src/cursor.ts` of double-pendulum-chaos, with per-group colors and a legend capped at 8 series): displacements, velocities, spring stretches, mode energies (log, last 60 s; values below 1e-12 of E left out), energies (E, T, V, W dissipated) and the energy balance error ((E + W - E0) / E0); others last 10 s; sampled every 10 ms; hover for values, click to pin. Mode bars, displacements and mode energies are on by default. Masses, springs and modes share one color per index (four fixed colors for up to 4, a blue-to-red sweep beyond).
5+Current state: N masses (2 to 64, default 2) between two fixed walls, joined by springs with tension k d + k3 d^3. The chain sits at rest until Strike, which gives it energy E (log slider, 10 µJ to 100 J, default 2.51 J) as velocity, either on one mass j alone (default mass 1, a hammer blow at one end) or in the shape of one normal mode n. The left card animates the chain (top: to scale, rest spacing a; below: the same displacements drawn sideways as a profile, scaled to fit, with the scale given) with the time and the relative energy error. Speed: 0.1x to 20x real time (default 1x). The time and current energy show at the top left. The Ghost chip (as in the pendulum app) adds a second chain started from the same state with every mass moved by the same amount, 10^k / sqrt(N) m (total offset 10^k m, k = -12 to -1, default -10; moving only the struck mass would put the offset along the motion itself, which does not grow), run in lockstep without renormalization and drawn in translucent orange underneath; restarted on Strike, on "Restart ghost" (from the chain's current state), when switched on or when the offset changes; a readout gives the separation (root sum of squared displacement differences, m) and the time since the ghost started, and a "Separation" chart (log, last 60 s; offered only with the ghost on) plots it. A "Lyapunov" chart (last 60 s; the first 5 s after a strike left out of its y range) shows the largest Lyapunov exponent over the last 10 s and since the strike (`lyapunov.ts`, two-trajectory Benettin method as in the pendulum app: a hidden shadow chain 1e-8 away, displacements in units of d0 and momenta in units of sqrt(m k) d0, renormalized every step). Checked (N = 4, mass-1 strike, 300 s): without damping 0.1 J ~0.05 to 0.1 /s (weak chaos; N = 4 onset is near 3 k d0^2 = 75 mJ), 0.5 J ~0.5 /s, 2.51 J ~0.9 /s, 25 J ~1.7 /s; with tau 540 s, 0.1 J falls to ~0.01 /s and 2.51 J falls from ~1.0 to ~0.3 /s by 300 s. The slider is labelled "Strike energy". Parameters (m, k, k3, a, tau) fold out underneath with derived values (mode frequencies, d0, nonlinear energy scale, integrator step); Apply or changing N returns the chain to rest. The "Real time" card on the right has toggle chips for its charts. "Mode history" (off by default) is a scrolling heat map of each mode's share of the harmonic energy, log10(E_n / sum E) over 6 decades (inferno colors, mode 1 at the bottom), over the last 300 s (`LiveModeHistory` in `modehistory.ts`); the cascade of energy up the modes shows here (e.g. N = 32, mode-1 strike at 10 mJ: the top mode's share goes from ~1e-28 to ~1e-12 in 10 periods of mode 1 and ~3e-5 in 50 without damping; with tau 540 s the damping wins and the spread stalls near mode 17). "Mode bars" shows the current harmonic energy of each normal mode relative to E on a log scale (12 decades), with the number of modes sharing the energy (inverse participation, 1 / sum (E_n / sum E)^2). The other chips are charts over time (from `traces.ts` and `cursor.ts` of double-pendulum-chaos, with per-group colors and a legend capped at 8 series): displacements, velocities, spring stretches, mode energies (log, last 60 s; values below 1e-12 of E left out), energies (E, T, V, W dissipated) and the energy balance error ((E + W - E0) / E0); others last 10 s; sampled every 10 ms; hover for values, click to pin. Mode bars, displacements and mode energies are on by default. Masses, springs and modes share one color per index (four fixed colors for up to 4, a blue-to-red sweep beyond).
66
7-"Sped up · listen" (open by default, below the cards; otherwise as in the pendulum app): the header says how much faster it runs ("the whole decay, 321× faster: 62 min of motion in 12 s"; without damping "321× faster than the chain above"). The page starts muted (the mute button is tinted red and reads "Muted"; muted notes are still computed and shown); the audio context is created at the first Strike; closing the panel stops the note. While it is open each Strike plays a note, the same strike rendered in a Web Worker (`src/render-worker.ts`) sped up so the lowest linear mode sounds at the Pitch (5 to 1000 Hz, default 100; below ~20 Hz the lowest modes are below hearing but show in the spectrogram; the readout gives the lowest and highest mode pitches). A damped note is the whole decay, ending when its energy has fallen to 1e-6 of the strike's (-60 dB; predicted length (tau/2) ln 1e6 of physical time, 11.6 s of sound by default, measured 11.3 s; capped at twice that); undamped notes last 10 s. "Strike to hear the whole decay." shows until the first note; the status line gives the signal and note length ("computing…" while rendering). Replay plays the last strike again with the current settings (to compare signals). Signal (`src/signals.ts`): velocity (default), displacement or acceleration of mass j (default 1, the end mass, which is never at a node), force on the left wall (the first spring's tension), or sum of accelerations (the net wall force, in which even modes cancel). In one mass's motion mode n is weighted by sqrt(E_n) / omega_n, sqrt(E_n) or sqrt(E_n) omega_n. Each note is made zero-mean, scaled to a 0.9 peak (all signals equally loud; Volume sets the level) and faded out over its last 20 ms (`src/sound.ts`). Save WAV saves the last note as heard (16-bit mono PCM, peak at 0.9; `src/wav.ts`; named e.g. fput-N4-mass1-2.51J-v1-100Hz.wav). Chips (as in the pendulum app, plus Modes): Spectrogram (on; pendulum's `NoteSpectrogram`: 2048-sample Hann, zero-pad 4096, hop 512, for pitches down to 30 Hz; below, the window lengthens in proportion (up to 16384 samples at 5 Hz, zero-pad 2x, hop a quarter window) so neighbouring low modes stay resolved; dashed lines at the linear mode pitches, drawn only while at least 6 px apart, since crowded together their dashes merged into a solid on-off bar at the bottom), Modes (off by default; the note's mode-energy history, each mode's share of the energy over the whole note, from the render worker, one column per 256 samples; `ModeHistory` in `src/modehistory.ts`; a cascade that takes tens of mode-1 periods occupies only the first few percent of a whole-decay note, so Mode history in the Real time panel shows it better), Lyapunov (the note's largest Lyapunov exponent over the last 10 s of physical time, from a shadow run in the worker alongside the note; the first 5 s of physical time left out of the y range), Energy (log scale, so the decay is a straight line) and λ vs E (`src/scatter.ts` from the pendulum app: one point per 256 samples, accumulated over notes, log energy axis, colored by time into the note on a log scale, earlier notes faded, hover for values, Clear button). The first three span the whole note (strike at left, end at right) and fill in as the audio clock reaches each sample; one shared cursor (playhead while playing; hover for values, click to pin). Pause (or space) pauses the animation and the sound (audio context suspended). The note's Lyapunov shadow runs only while the Lyapunov chart or λ vs E is shown (it doubles the computation). Render time in the browser pane (with the Lyapunov shadow): ~0.6 s for N = 4, ~2 s for N = 32 (default strike). First look (N = 4, 2.51 J): λ ~1.3 /s at the strike, falling to ~0 by ~4 s of sound as the spectrogram resolves into lines; on λ vs E the note's points sit near 0 below ~0.1 J and rise to ~1.4 /s at the top.
7+"Sped up · listen" (open by default, below the cards; otherwise as in the pendulum app): the header says how much faster it runs ("the whole decay, 321× faster: 62 min of motion in 12 s"; without damping "321× faster than the chain above"). The page starts muted (the mute button is tinted red and reads "Muted"; muted notes are still computed and shown); the audio context is created at the first Strike; closing the panel stops the note. While it is open each Strike plays a note, the same strike rendered in a Web Worker (`render-worker.ts`) sped up so the lowest linear mode sounds at the Pitch (5 to 1000 Hz, default 100; below ~20 Hz the lowest modes are below hearing but show in the spectrogram; the readout gives the lowest and highest mode pitches). A damped note is the whole decay, ending when its energy has fallen to 1e-6 of the strike's (-60 dB; predicted length (tau/2) ln 1e6 of physical time, 11.6 s of sound by default, measured 11.3 s; capped at twice that); undamped notes last 10 s. "Strike to hear the whole decay." shows until the first note; the status line gives the signal and note length ("computing…" while rendering). Replay plays the last strike again with the current settings (to compare signals). Signal (`signals.ts`): velocity (default), displacement or acceleration of mass j (default 1, the end mass, which is never at a node), force on the left wall (the first spring's tension), or sum of accelerations (the net wall force, in which even modes cancel). In one mass's motion mode n is weighted by sqrt(E_n) / omega_n, sqrt(E_n) or sqrt(E_n) omega_n. Each note is made zero-mean, scaled to a 0.9 peak (all signals equally loud; Volume sets the level) and faded out over its last 20 ms (`sound.ts`). Save WAV saves the last note as heard (16-bit mono PCM, peak at 0.9; `wav.ts`; named e.g. fput-N4-mass1-2.51J-v1-100Hz.wav). Chips (as in the pendulum app, plus Modes): Spectrogram (on; pendulum's `NoteSpectrogram`: 2048-sample Hann, zero-pad 4096, hop 512, for pitches down to 30 Hz; below, the window lengthens in proportion (up to 16384 samples at 5 Hz, zero-pad 2x, hop a quarter window) so neighbouring low modes stay resolved; dashed lines at the linear mode pitches, drawn only while at least 6 px apart, since crowded together their dashes merged into a solid on-off bar at the bottom), Modes (off by default; the note's mode-energy history, each mode's share of the energy over the whole note, from the render worker, one column per 256 samples; `ModeHistory` in `modehistory.ts`; a cascade that takes tens of mode-1 periods occupies only the first few percent of a whole-decay note, so Mode history in the Real time panel shows it better), Lyapunov (the note's largest Lyapunov exponent over the last 10 s of physical time, from a shadow run in the worker alongside the note; the first 5 s of physical time left out of the y range), Energy (log scale, so the decay is a straight line) and λ vs E (`scatter.ts` from the pendulum app: one point per 256 samples, accumulated over notes, log energy axis, colored by time into the note on a log scale, earlier notes faded, hover for values, Clear button). The first three span the whole note (strike at left, end at right) and fill in as the audio clock reaches each sample; one shared cursor (playhead while playing; hover for values, click to pin). Pause (or space) pauses the animation and the sound (audio context suspended). The note's Lyapunov shadow runs only while the Lyapunov chart or λ vs E is shown (it doubles the computation). Render time in the browser pane (with the Lyapunov shadow): ~0.6 s for N = 4, ~2 s for N = 32 (default strike). First look (N = 4, 2.51 J): λ ~1.3 /s at the strike, falling to ~0 by ~4 s of sound as the spectrogram resolves into lines; on λ vs E the note's points sit near 0 below ~0.1 J and rise to ~1.4 /s at the top.
88
9-Poincaré section (shown only for N = 2 and N = 3, below the sped-up panel, open; adapted from the pendulum app): its own energy slider (log, 10 µJ to 100 J, default 2.51 J), same parameters, no damping. N = 2: each time mass 1 passes its rest position moving right, mass 2's (u2, p2) is plotted. N = 3: only the symmetric family (u1 = u3, p1 = p3; only modes 1 and 3 move, since the cubic force couples odd modes only to odd modes), which is two-dimensional: each time mass 2 passes its rest position moving right, mass 1's (u1, p1) is plotted. On either section the springs are stretched by 0 or +-x, so V = vFactor (k x^2 / 2 + k3 x^4 / 4) (vFactor 2 or 4), the plotted momentum counts once (N = 2) or twice (N = 3) in the kinetic energy, the outline is where the crossing mass's momentum vanishes, and that momentum follows from E. 42 orbits seeded on a grid, 300 crossings each, in a worker (`src/poincare-core.ts`, `src/poincare-worker.ts`, `src/poincare.ts`; integrator step stepFor(E), energy error < 5e-5; ~1 s per section offline, a few s in the pane), drawn as they arrive; per-orbit colors or Color by λ (each orbit's Lyapunov exponent over its run, log scale); hover to pick out an orbit with its λ; click inside the outline to add an orbit there, and with Click to strike (on) also strike the chain from that exact state (and play it, if the sound panel is open). Follow (as in the pendulum app): "real time" makes the section's energy track the chain's as it decays (recomputed after a 3% change, at most every 2 s, and at each strike); "sped up" (which opens the sped-up panel) shows the playing note's crossings (found in the render worker, for N = 3 only while exactly symmetric, and drawn as the audio clock reaches them) as a fading trail of the last 150, with the axes fitted to the note's current energy (refit every 2%) and each dot fitted from its own energy; nothing is computed live. "Precompute sections" (shown only with Follow "sped up" until complete) builds an atlas (`src/atlas.ts`): sections at 32 energies log-spaced from 1 mJ to 100 J (a factor ~1.45 apart), 42 orbits x 150 crossings, highest first, in its own worker (~22 s in the pane for N = 3), stored in IndexedDB per N, m, k, k3 and deleted after 7 days; the sound view shows the atlas section nearest the current energy, fitted to its allowed region (exact in the linear regime, below ~25 mJ, where sections are scaled copies; an approximation across the gaps above). Without an atlas only the outline is drawn behind the trail. Setting the section's energy by hand pauses everything and clears the rings and the trail; Follow takes over again on Resume. The chain's own crossings show as dark rings when within 5% of the section's energy and, for N = 3, only while the chain is exactly symmetric (else the legend says "chain not symmetric, not shown"; strikes on mass 2 or on mode 1 or 3 are symmetric, mode shapes are made exactly mirror-symmetric for this). Findings: the two-mass chain appears regular at every energy tried (10 mJ to 100 J): nested closed curves and island chains, no chaotic sea; the grid orbits' λ falls as the run lengthens (at 10 J, median 0.045 /s over 300 crossings, 0.007 /s over 3000), as for regular orbits. A likely reason: in mode coordinates (u = S Q) the potential is (k/2) rho^2 + (k3/8) rho^4 with rho^2 = Q1^2 + 3 Q2^2, a function of one combination of the two modes. The N = 3 symmetric family is chaotic: grid orbits with λ > 0.1 /s: 0 of 42 at 25 mJ, 10 at 0.25 J (islands in a chaotic sea), 37 at 2.51 J. It is unstable to breaking the symmetry: an exactly symmetric start stays symmetric (the arithmetic is exactly mirror-symmetric), but an asymmetry of 1e-12 grows to full size within ~100 s at 0.25 to 25 J. The ghost and the Lyapunov shadow offset all masses equally, so after a symmetric strike they too stay symmetric and measure the exponent within the family.
9+Poincaré section (shown only for N = 2 and N = 3, below the sped-up panel, open; adapted from the pendulum app): its own energy slider (log, 10 µJ to 100 J, default 2.51 J), same parameters, no damping. N = 2: each time mass 1 passes its rest position moving right, mass 2's (u2, p2) is plotted. N = 3: only the symmetric family (u1 = u3, p1 = p3; only modes 1 and 3 move, since the cubic force couples odd modes only to odd modes), which is two-dimensional: each time mass 2 passes its rest position moving right, mass 1's (u1, p1) is plotted. On either section the springs are stretched by 0 or +-x, so V = vFactor (k x^2 / 2 + k3 x^4 / 4) (vFactor 2 or 4), the plotted momentum counts once (N = 2) or twice (N = 3) in the kinetic energy, the outline is where the crossing mass's momentum vanishes, and that momentum follows from E. 42 orbits seeded on a grid, 300 crossings each, in a worker (`poincare-core.ts`, `poincare-worker.ts`, `poincare.ts`; integrator step stepFor(E), energy error < 5e-5; ~1 s per section offline, a few s in the pane), drawn as they arrive; per-orbit colors or Color by λ (each orbit's Lyapunov exponent over its run, log scale); hover to pick out an orbit with its λ; click inside the outline to add an orbit there, and with Click to strike (on) also strike the chain from that exact state (and play it, if the sound panel is open). Follow (as in the pendulum app): "real time" makes the section's energy track the chain's as it decays (recomputed after a 3% change, at most every 2 s, and at each strike); "sped up" (which opens the sped-up panel) shows the playing note's crossings (found in the render worker, for N = 3 only while exactly symmetric, and drawn as the audio clock reaches them) as a fading trail of the last 150, with the axes fitted to the note's current energy (refit every 2%) and each dot fitted from its own energy; nothing is computed live. "Precompute sections" (shown only with Follow "sped up" until complete) builds an atlas (`atlas.ts`): sections at 32 energies log-spaced from 1 mJ to 100 J (a factor ~1.45 apart), 42 orbits x 150 crossings, highest first, in its own worker (~22 s in the pane for N = 3), stored in IndexedDB per N, m, k, k3 and deleted after 7 days; the sound view shows the atlas section nearest the current energy, fitted to its allowed region (exact in the linear regime, below ~25 mJ, where sections are scaled copies; an approximation across the gaps above). Without an atlas only the outline is drawn behind the trail. Setting the section's energy by hand pauses everything and clears the rings and the trail; Follow takes over again on Resume. The chain's own crossings show as dark rings when within 5% of the section's energy and, for N = 3, only while the chain is exactly symmetric (else the legend says "chain not symmetric, not shown"; strikes on mass 2 or on mode 1 or 3 are symmetric, mode shapes are made exactly mirror-symmetric for this). Findings: the two-mass chain appears regular at every energy tried (10 mJ to 100 J): nested closed curves and island chains, no chaotic sea; the grid orbits' λ falls as the run lengthens (at 10 J, median 0.045 /s over 300 crossings, 0.007 /s over 3000), as for regular orbits. A likely reason: in mode coordinates (u = S Q) the potential is (k/2) rho^2 + (k3/8) rho^4 with rho^2 = Q1^2 + 3 Q2^2, a function of one combination of the two modes. The N = 3 symmetric family is chaotic: grid orbits with λ > 0.1 /s: 0 of 42 at 25 mJ, 10 at 0.25 J (islands in a chaotic sea), 37 at 2.51 J. It is unstable to breaking the symmetry: an exactly symmetric start stays symmetric (the arithmetic is exactly mirror-symmetric), but an asymmetry of 1e-12 grows to full size within ~100 s at 0.25 to 25 J. The ghost and the Lyapunov shadow offset all masses equally, so after a symmetric strike they too stay symmetric and measure the exponent within the family.
1010
11-Each card and panel (chain, Parameters, Real time, Sped up · listen, Poincaré section) has a small "?" button that opens a short help dialog (`src/help.ts` from the pendulum app; the texts are `<template>`s in `index.html`).
11+Each card and panel (chain, Parameters, Real time, Sped up · listen, Poincaré section) has a small "?" button that opens a short help dialog (`help.ts` from the pendulum app; the texts are `<template>`s in `index.html`).
1212
13-Live site: https://fput-chain-chaos--listening-for-chaos.vault1-sites.magland.org/ (built and deployed by `.github/workflows/pages.yml` on every push to main).
13+Live site: https://chaos-apps--listening-for-chaos.vault1-sites.magland.org/fput-chain/ (part of the chaos-apps site; see the top-level README).
1414
15-```bash
16-npm install
17-npm run dev
18-```
1915
2016 ## Settings to try
2117
@@ -30,4 +26,4 @@ npm run dev
3026 - Chaos for N = 4 (two-trajectory Lyapunov, 5000 / sqrt(k/m) s): none below E ~ 1 k d0^2; onset near 3 k d0^2; at the default 10 k d0^2, lambda ~0.1 sqrt(k/m) = 0.32 /s, energy shared by ~2.5 modes.
3127 - Symmetry: the quartic force couples odd modes only to odd modes, so a strike in an odd mode leaves the even modes at exactly zero (they stay off the bottom of the chart). A strike on one mass, other than the middle one, excites all modes.
3228
33-Code: `src/chain.ts` (physics, modes, strikes), `src/main.ts` (page), `src/traces.ts` and `src/cursor.ts` (charts), `src/signals.ts`, `src/render-worker.ts`, `src/sound.ts`, `src/spectrogram.ts`, `src/scatter.ts` (sound), `src/lyapunov.ts` (Lyapunov exponent, animation and sound), `src/poincare*.ts` (section, N = 2 and 3).
29+Code: `chain.ts` (physics, modes, strikes), `main.ts` (page), `traces.ts` and `cursor.ts` (charts), `signals.ts`, `render-worker.ts`, `sound.ts`, `spectrogram.ts`, `scatter.ts` (sound), `lyapunov.ts` (Lyapunov exponent, animation and sound), `poincare*.ts` (section, N = 2 and 3).
src/fput-chain/index.htmlmodified+1−1View file
@@ -277,6 +277,6 @@
277277 <p>A map of the motions at one energy, without damping. Two masses: each time mass 1 passes its rest position moving right, mass 2's displacement and momentum are plotted. Three masses: only motions with masses 1 and 3 moving alike, plotted each time mass 2 passes rest. Regular motions trace closed curves; chaotic ones scatter. The outline bounds what is reachable.</p>
278278 <p>The chain's own crossings are dark rings. Follow tracks the chain (real time) or the playing note (sped up). Hover to pick out an orbit; click to start one there, which with Click to strike also strikes the chain.</p>
279279 </template>
280- <script type="module" src="/src/main.ts"></script>
280+ <script type="module" src="./main.ts"></script>
281281 </body>
282282 </html>
src/fput-chain/tsconfig.jsondeleted+0−12View file
@@ -1,12 +0,0 @@
1-{
2- "compilerOptions": {
3- "target": "ES2022",
4- "module": "ESNext",
5- "moduleResolution": "bundler",
6- "lib": ["ES2022", "DOM"],
7- "strict": true,
8- "noEmit": true,
9- "skipLibCheck": true
10- },
11- "include": ["src", "vite.config.ts"]
12-}
src/fput-chain/vite.config.tsdeleted+0−8View file
@@ -1,8 +0,0 @@
1-import { defineConfig } from "vite";
2-
3-// Relative asset paths, so the build works wherever the site is served
4-// (an origin root or a /<collection>/<repo>/site/ path); ES module workers.
5-export default defineConfig({
6- base: "./",
7- worker: { format: "es" },
8-});
src/index.htmladded+39−0View file
@@ -0,0 +1,39 @@
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.0" />
6+ <title>Listening for chaos</title>
7+ <style>
8+ :root {
9+ --bg: #f6f6f3;
10+ --card: #ffffff;
11+ --line: #e3e2dc;
12+ --text: #23262b;
13+ --muted: #7a7d82;
14+ --accent: #2f5d8a;
15+ --serif: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
16+ --sans: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
17+ }
18+ * { box-sizing: border-box; }
19+ body { margin: 0; background: var(--bg); color: var(--text); font: 14px/1.45 var(--sans); }
20+ main { max-width: 720px; margin: 0 auto; padding: 28px 16px 48px; }
21+ h1 { font: 400 28px/1.2 var(--serif); margin: 0 0 4px; letter-spacing: 0.01em; }
22+ .muted { color: var(--muted); }
23+ ul { list-style: none; padding: 0; margin: 20px 0 0; display: grid; gap: 12px; }
24+ a { display: block; background: var(--card); border: 1px solid var(--line); border-radius: 10px; padding: 14px 16px; color: inherit; text-decoration: none; }
25+ a:hover { border-color: var(--accent); }
26+ a strong { color: var(--accent); font-weight: 600; }
27+ </style>
28+ </head>
29+ <body>
30+ <main>
31+ <h1>Listening for chaos</h1>
32+ <div class="muted">Struck dynamical systems heard as sound while they decay. Work in progress.</div>
33+ <ul>
34+ <li><a href="./double-pendulum/"><strong>Double pendulum</strong><div class="muted">Strike it, watch and hear the motion turn regular as the energy decays; Lyapunov exponent and Poincaré section.</div></a></li>
35+ <li><a href="./fput-chain/"><strong>FPUT chain</strong><div class="muted">A struck chain of N masses with cubic springs; energy spreading up the modes, Lyapunov exponent and Poincaré sections for N = 2 and 3.</div></a></li>
36+ </ul>
37+ </main>
38+ </body>
39+</html>
src/double-pendulum/tsconfig.json →tsconfig.jsonrenamed+0−0View file
No changes to the file's content.
vite.config.tsadded+24−0View file
@@ -0,0 +1,24 @@
1+import { defineConfig } from "vite";
2+
3+// One multi-page site: src/index.html (landing page) and one page per app in
4+// src/<app>/index.html, served at /<app>/. Shared code goes in src/common/.
5+// Relative asset paths, so the build works wherever the site is served
6+// (an origin root or a /<collection>/<repo>/site/ path); ES module workers.
7+const page = (p: string) => decodeURIComponent(new URL(`./src/${p}`, import.meta.url).pathname);
8+
9+export default defineConfig({
10+ root: "src",
11+ base: "./",
12+ worker: { format: "es" },
13+ build: {
14+ outDir: "../dist",
15+ emptyOutDir: true,
16+ rolldownOptions: {
17+ input: {
18+ main: page("index.html"),
19+ "double-pendulum": page("double-pendulum/index.html"),
20+ "fput-chain": page("fput-chain/index.html"),
21+ },
22+ },
23+ },
24+});
↑↓ move↵openescclose