nordic-skating / files / index.html
index.html
c90277fa 2026-10-09 Pavel Safronov | 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, viewport-fit=cover" /> | |
| 6 | <title>Nordic Skating</title> | |
bd311616 2026-10-10 Pavel Safronov | 7 | <meta name="description" content="An endless skate over thin black ice that sings under every stride." /> |
| 8 | <meta name="theme-color" content="#0b1a24" /> | |
| 9 | <!-- For an iPhone: the icon it puts on the home screen, and to open from there without Safari's bars. --> | |
| 10 | <link rel="apple-touch-icon" href="/apple-touch-icon.png" /> | |
| 11 | <meta name="mobile-web-app-capable" content="yes" /> | |
| 12 | <meta name="apple-mobile-web-app-capable" content="yes" /> | |
| 13 | <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" /> | |
| 14 | <!-- The tab's icon: white lines where the browser is dark, black where it is light. A | |
| 15 | browser that takes no notice of `media` takes the last, which is the black. --> | |
| 16 | <link rel="icon" type="image/png" href="/favicon-dark.png" media="(prefers-color-scheme: dark)" /> | |
| 17 | <link rel="icon" type="image/png" href="/favicon.png" media="(prefers-color-scheme: light)" /> | |
c90277fa 2026-10-09 Pavel Safronov | 18 | </head> |
| 19 | <body> | |
| 20 | <canvas id="scene"></canvas> | |
95f8bb94 2026-10-09 Pavel Safronov | 21 | <nav id="links"> |
a8fd3f13 2026-10-10 Pavel Safronov | 22 | <button id="about" type="button">About</button> |
| 23 | <button id="settings" type="button">Settings</button> | |
| 24 | <button id="randomize" type="button">Randomize</button> | |
95f8bb94 2026-10-09 Pavel Safronov | 25 | </nav> |
| 26 | <button id="sound" type="button"></button> | |
1ee103b4 2026-10-11 Pavel Safronov | 27 | <button id="pace-button" type="button"></button> |
4efa2e9a 2026-10-10 Pavel Safronov | 28 | <!-- Shown only on what can be tilted (main.ts). --> |
| 29 | <button id="tilt-button" type="button" hidden></button> | |
a8fd3f13 2026-10-10 Pavel Safronov | 30 | <dialog id="about-dialog" aria-labelledby="about-title"> |
| 31 | <h1 id="about-title" tabindex="-1" autofocus>Nordic Skating</h1> | |
| 32 | <p>An endless skate over thin black ice. The ice sings under every stride, and the thinner it is, the higher it sings. Leave it alone and it skates itself, for as long as you like.</p> | |
| 33 | ||
| 34 | <h2>Controls</h2> | |
| 35 | <dl> | |
| 36 | <dt>Steer</dt> | |
c2870e67 2026-10-10 Pavel Safronov | 37 | <dd>Drag left or right from wherever you touch, or use ← → or A D; or, on a phone, press Tilt at the bottom left and lean it. Let go and the skater carries on the way you sent it, curving gently on from there; it does not go back to where it was. Hold it over and the skater turns right round: there is room for a circle wherever the water is wide.</dd> |
a8fd3f13 2026-10-10 Pavel Safronov | 38 | <dt>Choose a way at a fork</dt> |
4f816cf0 2026-10-10 Pavel Safronov | 39 | <dd>A touch towards it on the way up to the fork is enough. Do nothing and the skater picks one.</dd> |
| 40 | <dt>Islets</dt> | |
| 41 | <dd>Tufts of reeds standing in the ice. The skater goes round them unasked; you can weave between them, or circle one.</dd> | |
1ee103b4 2026-10-11 Pavel Safronov | 42 | <dt>Go faster or slower</dt> |
| 43 | <dd>The button at the bottom right steps through five paces, from a meander to an Olympic sprint; or use ↑ ↓ or W S.</dd> | |
a8fd3f13 2026-10-10 Pavel Safronov | 44 | <dt>Sound on or off</dt> |
| 45 | <dd>The button at the top right, or M.</dd> | |
8bb08e4a 2026-10-10 Pavel Safronov | 46 | <dt>With a game controller</dt> |
1ee103b4 2026-10-11 Pavel Safronov | 47 | <dd>Either stick or the direction buttons steer; up and down on the direction buttons, or the shoulder buttons, change pace. Y or triangle goes to another lake; X or square turns the sound on and off.</dd> |
a8fd3f13 2026-10-10 Pavel Safronov | 48 | <dt>Another lake</dt> |
| 49 | <dd>Randomize, at the top left, or R. The browser’s Back button returns to the lake you left.</dd> | |
| 50 | <dt>Time of day, and more</dt> | |
236f7d03 2026-10-10 Pavel Safronov | 51 | <dd>Settings, at the top left: day, dusk, night under the Milky Way, or the northern lights; clear, snowing, or a blizzard. Left alone, every lake has a time and a weather of its own.</dd> |
a8fd3f13 2026-10-10 Pavel Safronov | 52 | </dl> |
| 53 | <p>Every visit is to a different lake, one of a million.</p> | |
| 54 | ||
| 55 | <h2>The ice</h2> | |
| 56 | <dl> | |
| 57 | <dt>Milky grey</dt> | |
| 58 | <dd>Thick ice. A low, plain note.</dd> | |
| 59 | <dt>Dark grey</dt> | |
| 60 | <dd>Thinner ice, near its limit. This is the ice that sings.</dd> | |
| 61 | <dt>Nearly black</dt> | |
| 62 | <dd>Thin ice. A high note, and it cracks around you.</dd> | |
d94fe229 2026-10-10 Pavel Safronov | 63 | <dt class="impossible-only">Bright pink</dt> |
a8fd3f13 2026-10-10 Pavel Safronov | 64 | <dd class="impossible-only">Impossibly thin ice. A very high note, and no cracks at all.</dd> |
| 65 | </dl> | |
| 66 | <p>Nobody falls through. How thick the ice is decides only how it sounds.</p> | |
| 67 | ||
| 68 | <h2>The sound</h2> | |
| 69 | <p>None of it is a recording. It is synthesized from the physics of thin ice: the note is the one frequency at which a sheet of that thickness gives its sound to the air, and the falling “pew” is a distant crack, its high notes arriving first.</p> | |
| 70 | <p>Inspired by National Geographic’s film <a href="https://www.youtube.com/watch?v=v3O9vNi-dkA" target="_blank" rel="noopener">Hear the Otherworldly Sounds of Skating on Thin Ice</a>.</p> | |
bcbc446b 2026-10-27 Pavel Safronov | 71 | <p><a href="https://code.fuzzyworld.net/nordic-skating/blob/README.md.html" target="_blank" rel="noopener">How it works, and credits</a> · <a href="https://code.fuzzyworld.net/nordic-skating/" target="_blank" rel="noopener">Source</a></p> |
bd311616 2026-10-10 Pavel Safronov | 72 | <!-- The Flaticon free licence requires the credit wherever the artwork is used. --> |
1ee103b4 2026-10-11 Pavel Safronov | 73 | <p><small>Icons by <a href="https://www.flaticon.com/authors/icongeek26" target="_blank" rel="noopener">Icongeek26</a>, from Flaticon.</small></p> |
a8fd3f13 2026-10-10 Pavel Safronov | 74 | |
| 75 | <form method="dialog"><button id="about-close" class="done">Skate on</button></form> | |
| 76 | </dialog> | |
| 77 | <dialog id="settings-dialog" aria-labelledby="settings-title"> | |
| 78 | <h1 id="settings-title" tabindex="-1" autofocus>Settings</h1> | |
| 79 | ||
| 80 | <fieldset> | |
| 81 | <legend>Time of day</legend> | |
| 82 | <label><input type="radio" name="time" value="surprise" /> Surprise me <small>each lake has its own</small></label> | |
| 83 | <label><input type="radio" name="time" value="day" /> Day</label> | |
| 84 | <label><input type="radio" name="time" value="dusk" /> Dusk</label> | |
| 85 | <label><input type="radio" name="time" value="night" /> Night <small>under the Milky Way</small></label> | |
| 86 | <label><input type="radio" name="time" value="aurora" /> Aurora <small>the northern lights</small></label> | |
| 87 | </fieldset> | |
| 88 | ||
1ee103b4 2026-10-11 Pavel Safronov | 89 | <fieldset> |
| 90 | <legend>Pace</legend> | |
| 91 | <label><input type="radio" name="pace" value="meander" /> Meander <small>13 km/h, in long lazy strides</small></label> | |
| 92 | <label><input type="radio" name="pace" value="cruise" /> Cruise <small>25 km/h</small></label> | |
| 93 | <label><input type="radio" name="pace" value="brisk" /> Brisk <small>36 km/h</small></label> | |
| 94 | <label><input type="radio" name="pace" value="racing" /> Racing <small>45 km/h</small></label> | |
| 95 | <label><input type="radio" name="pace" value="olympic" /> Olympic <small>54 km/h, as if training for the Games</small></label> | |
| 96 | </fieldset> | |
| 97 | ||
236f7d03 2026-10-10 Pavel Safronov | 98 | <fieldset> |
| 99 | <legend>Weather</legend> | |
| 100 | <label><input type="radio" name="weather" value="surprise" /> Surprise me <small>each lake has its own</small></label> | |
| 101 | <label><input type="radio" name="weather" value="clear" /> Clear</label> | |
| 102 | <label><input type="radio" name="weather" value="snow" /> Snow <small>falling quietly</small></label> | |
| 103 | <label><input type="radio" name="weather" value="blizzard" /> Blizzard <small>driven by a high wind</small></label> | |
| 104 | </fieldset> | |
| 105 | ||
a8fd3f13 2026-10-10 Pavel Safronov | 106 | <fieldset> |
| 107 | <legend>Ice</legend> | |
d94fe229 2026-10-10 Pavel Safronov | 108 | <label><input type="checkbox" id="impossible" /> Impossible ice <small>patches thinner than any ice could be: a very high note, bright pink, no cracks</small></label> |
a8fd3f13 2026-10-10 Pavel Safronov | 109 | </fieldset> |
| 110 | ||
| 111 | <fieldset> | |
| 112 | <legend>Lake</legend> | |
| 113 | <p>This is lake number <a id="lake-link"><span id="lake-number"></span></a> of a million. Every visit is to another; the link comes back to this one.</p> | |
| 114 | <button id="new-lake" type="button">Another lake</button> | |
| 115 | </fieldset> | |
| 116 | ||
| 117 | <form method="dialog"><button class="done">Done</button></form> | |
| 118 | </dialog> | |
| 119 | <!-- Hidden until the stylesheet has arrived to hide it properly: without this its words show for a moment as the page loads. --> | |
| 120 | <div id="water" hidden><p>The ice gave way</p></div> | |
| 121 | <div id="veil"></div> | |
95f8bb94 2026-10-09 Pavel Safronov | 122 | <p id="hint">Drag left or right to steer — or just watch</p> |
c90277fa 2026-10-09 Pavel Safronov | 123 | <script type="module" src="/src/main.ts"></script> |
| 124 | </body> | |
| 125 | </html> |