paperlab 0.8.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +57 -30
  2. package/dist/{FxPostPass-V6YDK4H2.js → FxPostPass-ACFSHY3S.js} +4 -4
  3. package/dist/FxPostPass-ACFSHY3S.js.map +1 -0
  4. package/dist/{chunk-232SU3AQ.js → chunk-BIFUJCUA.js} +2 -2
  5. package/dist/{chunk-232SU3AQ.js.map → chunk-BIFUJCUA.js.map} +1 -1
  6. package/dist/{chunk-SVWWC5UL.js → chunk-LUKNPQ3S.js} +16 -17
  7. package/dist/chunk-LUKNPQ3S.js.map +1 -0
  8. package/dist/{chunk-7WOGU2D4.js → chunk-S74YZAXE.js} +2 -2
  9. package/dist/chunk-S74YZAXE.js.map +1 -0
  10. package/dist/{damageContract-nF1fgK7P.d.cts → damageContract-9RC4egJg.d.cts} +2 -2
  11. package/dist/{damageContract-nF1fgK7P.d.ts → damageContract-9RC4egJg.d.ts} +2 -2
  12. package/dist/fx.cjs +20 -20
  13. package/dist/fx.cjs.map +1 -1
  14. package/dist/fx.d.cts +42 -44
  15. package/dist/fx.d.ts +42 -44
  16. package/dist/fx.js +21 -21
  17. package/dist/fx.js.map +1 -1
  18. package/dist/index.cjs +22 -23
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +4 -4
  21. package/dist/index.d.ts +4 -4
  22. package/dist/index.js +6 -6
  23. package/dist/index.js.map +1 -1
  24. package/dist/{slots-D_EE8GXn.d.cts → slots-DbJQfwVd.d.cts} +1 -1
  25. package/dist/{slots-D_EE8GXn.d.ts → slots-DbJQfwVd.d.ts} +1 -1
  26. package/dist/stage.cjs +23 -24
  27. package/dist/stage.cjs.map +1 -1
  28. package/dist/stage.d.cts +1 -1
  29. package/dist/stage.d.ts +1 -1
  30. package/dist/stage.js +12 -12
  31. package/dist/stage.js.map +1 -1
  32. package/package.json +5 -5
  33. package/dist/FxPostPass-V6YDK4H2.js.map +0 -1
  34. package/dist/chunk-7WOGU2D4.js.map +0 -1
  35. package/dist/chunk-SVWWC5UL.js.map +0 -1
package/dist/fx.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { D as DamageSource } from './damageContract-nF1fgK7P.cjs';
2
- export { a as DAMAGE_CHANNELS, b as DAMAGE_LOOK_DEFAULTS, c as DamageLook } from './damageContract-nF1fgK7P.cjs';
1
+ import { D as DamageSource } from './damageContract-9RC4egJg.cjs';
2
+ export { a as DAMAGE_CHANNELS, b as DAMAGE_LOOK_DEFAULTS, c as DamageLook } from './damageContract-9RC4egJg.cjs';
3
3
  import * as react from 'react';
4
4
  import { ReactNode } from 'react';
5
5
  import * as THREE from 'three';
@@ -366,7 +366,7 @@ declare const particlePresets: {
366
366
  * caught the bug in the version before this one. That version said "the hot
367
367
  * end is now ~5.6, well over the bloom threshold" — and 5.6 was an ABSOLUTE
368
368
  * luminance, while paper white under `window` is 1.6. So the hottest spark
369
- * in the frame was **3.5× paper, under the spec's own 4× floor**, and its
369
+ * in the frame was **3.5× paper, under the 4× floor**, and its
370
370
  * own comment said otherwise. A number that cannot be compared to anything
371
371
  * is a number nobody can check.
372
372
  *
@@ -540,8 +540,8 @@ interface FireEmitterOptions {
540
540
  };
541
541
  }
542
542
  /**
543
- * How much each kind leaves the front, per texel per second — Noor's tune in
544
- * the lab on 2026-09-13, which the lab starts from and `/hands` inherits.
543
+ * How much each kind leaves the front, per texel per second — the tune made in
544
+ * the lab, which the lab starts from and `/hands` inherits.
545
545
  *
546
546
  * The simulator's smoke is the main smoke of a burning sheet, since it is the
547
547
  * same fluid the flames are made of; these sprite puffs, once switched off
@@ -576,8 +576,8 @@ declare class FireEmitter {
576
576
  }
577
577
 
578
578
  /**
579
- * The pool, drawn — three kinds, three ways, because the spec's three
580
- * particles are three different things (paperlab-fx-fire-spec.md §8).
579
+ * The pool, drawn — three kinds, three ways, because a fire's three
580
+ * particles are three different things.
581
581
  *
582
582
  * - **Embers** are light, moving fast: quads STRETCHED along their own
583
583
  * screen-space velocity into streaks, a bright head and a fading tail,
@@ -655,21 +655,21 @@ interface FxQualitySettings {
655
655
  * Bloom is a blur, and a blur at half resolution is a quarter of the
656
656
  * fill-rate for a glow that is soft anyway — the cheapest thing a phone
657
657
  * can give up that still leaves every zone and every flame on screen
658
- * (spec §12: tiers thin post quality, never what is shown). Read by
658
+ * (tiers thin post quality, never what is shown). Read by
659
659
  * `FxPost`.
660
660
  */
661
661
  bloomScale: number;
662
662
  /**
663
- * The most flames standing at once. The spec's §12 said 8 / 16 / 32; a ring
663
+ * The most flames standing at once. It was 8 / 16 / 32; a ring
664
664
  * of fire that is dense in clusters and broken by gaps needs more tongues
665
- * than that, and Noor's direction (a living, uneven ring, never a crown)
666
- * came later and wins — 16 / 36 / 64. Every tier has real flames; a phone
665
+ * than that (a living, uneven ring, never a crown), so it is
666
+ * 16 / 36 / 64. Every tier has real flames; a phone
667
667
  * gets fewer tongues, never the cheap version.
668
668
  * Read by `FxFlames`.
669
669
  */
670
670
  flames: number;
671
671
  /**
672
- * The most of each particle kind in the air at once (spec §12), inside the
672
+ * The most of each particle kind in the air at once, inside the
673
673
  * `particles` budget. Read by `FireEmitter` through its `caps` option.
674
674
  */
675
675
  caps: {
@@ -681,7 +681,7 @@ interface FxQualitySettings {
681
681
  * Heat haze strength in pixels at 1080p; 0 turns it off. Read by `FxPost`.
682
682
  *
683
683
  * High tier only, and half what it was. It was 3.8 px on two tiers against
684
- * §10's 1–3, and invisible in every capture at either — a few pixels of
684
+ * the 1–3 wanted, and invisible in every capture at either — a few pixels of
685
685
  * wobble reads as nothing while the fluid beside it is moving. It is also
686
686
  * still driven from `flameAnchors`, which is where the SPRITE flames stand,
687
687
  * not where the fluid actually burns; until it reads the fluid's own heat
@@ -731,7 +731,7 @@ interface FxPostProps {
731
731
  threshold?: number;
732
732
  /**
733
733
  * The burning field, and where its sheet is — for the heat haze above its
734
- * flames and the frame's warm grade as it grows (§7). Leave both out and
734
+ * flames and the frame's warm grade as it grows. Leave both out and
735
735
  * the pass is bloom and the tone curve alone.
736
736
  */
737
737
  field?: DamageField;
@@ -762,7 +762,7 @@ declare function FxPost(props: FxPostProps): react.JSX.Element;
762
762
  * were identical: most presets are safe from 0.8–0.9, `studio` from 1.1, and
763
763
  * `window` — the brightest — only from 1.6. The brightest preset has to set
764
764
  * this, because a threshold that lets paper bloom under any preset brings back
765
- * the painted-glow failure of §0.
765
+ * the painted-glow failure of the first fire.
766
766
  *
767
767
  * Re-measure it with `pnpm test:fire-look` if a preset's lighting changes.
768
768
  */
@@ -775,14 +775,14 @@ declare const PAPER_WHITE = 1.6;
775
775
  * the number survives: it is not "3.6", it is "clear of the brightest paper".
776
776
  *
777
777
  * **Do not lower it to make fire bloom.** That is the wrong end of the
778
- * problem, and the review that found the fluid's flames under it said so:
778
+ * problem, and a visual review that found the fluid's flames under it said so:
779
779
  * `window`'s paper blooms from 1.6, so lowering the threshold blooms paper,
780
780
  * which is the painted glow again. Raise what the fire EMITS.
781
781
  */
782
782
  declare const FX_BLOOM_THRESHOLD: number;
783
783
  /**
784
784
  * What the spec asks a glowing part of a fire to reach, in multiples of paper
785
- * white (§4.3). The band `emission.test.ts` holds every emitter to.
785
+ * white. The band `emission.test.ts` holds every emitter to.
786
786
  */
787
787
  declare const FIRE_GLOW: readonly [4, 8];
788
788
  /** One sRGB channel (0..1) to linear. The transfer function, not a gamma. */
@@ -845,8 +845,8 @@ declare const FIRE_SOOT_SCALE = 3;
845
845
  * multiplier on whatever clears the threshold, so its right value depends
846
846
  * entirely on how much fire is authored above it. With a core authored to
847
847
  * over-expose, 0.55 was the most the black stage could take; the flame of
848
- * {@link FIRE_ZONES} (Noor, 2026-09-13) authors none of it past the
849
- * threshold, and 1.25 is her tune for it. `pnpm test:fire-budget` holds the
848
+ * {@link FIRE_ZONES} authors none of it past the
849
+ * threshold, and 1.25 is the tune for it. `pnpm test:fire-budget` holds the
850
850
  * stage black and the paper unbloomed whatever this is set to.
851
851
  */
852
852
  declare const FX_BLOOM = 1.25;
@@ -882,7 +882,7 @@ declare const FX_BLOOM = 1.25;
882
882
  * both ways. Above it, a flame keeps its light in a bright room but loses its
883
883
  * colour to the tone curve; below it, the colour holds, and a flame drawn
884
884
  * opaque risks reading as a decal on the sheet rather than as light. The
885
- * defaults sit below it (Noor, 2026-09-13), with the bloom carrying the glow.
885
+ * defaults sit below it, with the bloom carrying the glow.
886
886
  */
887
887
  interface FireZones {
888
888
  root: {
@@ -913,7 +913,7 @@ type FireZonesInput = {
913
913
  [Z in keyof FireZones]?: Partial<FireZones[Z]>;
914
914
  };
915
915
  /**
916
- * Noor's tune in the lab on 2026-09-13, and the source of truth for how a
916
+ * The tune made in `/fx-lab`, and the source of truth for how a
917
917
  * flame looks: the lab starts from it and `/hands` inherits it. A gentler
918
918
  * flame than the one it replaced — every zone sits under the bloom threshold,
919
919
  * so its glow comes from the bloom's strength ({@link FX_BLOOM}) spreading
@@ -938,9 +938,9 @@ interface AfterglowOptions {
938
938
  /**
939
939
  * The embers a burn leaves behind, drawn over the field it came from.
940
940
  *
941
- * `paperlab-fx-fire-spec.md` §9, "Smoulder": after the flames, glowing spots
941
+ * Smoulder: after the flames, glowing spots
942
942
  * crawl along the edge, flare on a breath, and go out one by one over
943
- * seconds (§4.10). The field cannot do this, and it is not a tuning problem:
943
+ * seconds. The field cannot do this, and it is not a tuning problem:
944
944
  * a burn blown out loses its heat in a fraction of a second — below ignition
945
945
  * everywhere or it recovers — so its own heat channel has nothing left to
946
946
  * smoulder with.
@@ -954,7 +954,7 @@ interface AfterglowOptions {
954
954
  * breath flares what is left.
955
955
  *
956
956
  * Only the sheet reads it. Flames and the fire light read the FIELD, so the
957
- * flames die with the gas (§4.1) while the edge keeps glowing; the physics
957
+ * flames die with the gas while the edge keeps glowing; the physics
958
958
  * reads char, saturation and presence, which pass through untouched.
959
959
  */
960
960
  declare class Afterglow implements DamageSource {
@@ -977,7 +977,7 @@ declare class Afterglow implements DamageSource {
977
977
  get smouldering(): boolean;
978
978
  /**
979
979
  * Advance by `dt`, after the field has stepped. `blow` is the breath on the
980
- * sheet, 0..1: it flares what is left (§10.6, "on smoulder: beads flare,
980
+ * sheet, 0..1: it flares what is left ("on smoulder: beads flare,
981
981
  * then fade").
982
982
  */
983
983
  step(dt: number, blow?: number): void;
@@ -994,7 +994,7 @@ interface FxFlamesProps {
994
994
  quality?: FxQualityTier;
995
995
  /**
996
996
  * The air, world units a second — pass the particle pool's `wind`, which is
997
- * read every frame. Flames lean away from it and shorten (§10.6).
997
+ * read every frame. Flames lean away from it and shorten.
998
998
  */
999
999
  wind?: readonly [number, number, number];
1000
1000
  }
@@ -1002,8 +1002,8 @@ interface FxFlamesProps {
1002
1002
  * The flames of a burn: separate tongues of burning gas, standing just off
1003
1003
  * the char, rising straight up whatever the sheet is doing.
1004
1004
  *
1005
- * `paperlab-fx-fire-spec.md` §6. Each tongue is a quad that turns to face the
1006
- * camera about WORLD up — never the sheet's normal (§13.9) — so a flame on a
1005
+ * Each tongue is a quad that turns to face the
1006
+ * camera about WORLD up — never the sheet's normal — so a flame on a
1007
1007
  * sheet held flat still rises to the ceiling. The shader is upward-scrolling,
1008
1008
  * domain-warped noise inside a teardrop, coloured on the flame's own
1009
1009
  * blackbody ramp: a dim indigo root, a yellow-white core, orange edges and
@@ -1042,8 +1042,8 @@ declare function FxWisps({ glow, field, locate, wind }: FxWispsProps): react.JSX
1042
1042
 
1043
1043
  /**
1044
1044
  * The fire simulator's controls — by the names, and at the defaults, of the
1045
- * fluid-fire tool Noor pointed at (Emission, Combustion, Fuel & air, Motion
1046
- * & turbulence), so the lab's panel reads like the one she knows.
1045
+ * fluid-fire tool it was modelled on (Emission, Combustion, Fuel & air, Motion
1046
+ * & turbulence), so the lab's panel reads like that one.
1047
1047
  *
1048
1048
  * The numbers are the panel's, not the solver's. `solverUniforms` is the one
1049
1049
  * place they are turned into this solver's units — world units (a default
@@ -1102,8 +1102,7 @@ interface FireFluidParams {
1102
1102
  smokeAfter: number;
1103
1103
  }
1104
1104
  /**
1105
- * The defaults — the panel's names, at the values Noor tuned in the lab on
1106
- * 2026-09-12. No turbulence of its own: the motion comes from vorticity,
1105
+ * The defaults — the panel's names, at the values tuned in the lab. No turbulence of its own: the motion comes from vorticity,
1107
1106
  * the radial impulse and the uneven rim that feeds it.
1108
1107
  */
1109
1108
  declare const fireFluidDefaults: FireFluidParams;
@@ -1327,7 +1326,7 @@ interface FxFireLightProps {
1327
1326
  shadows?: boolean;
1328
1327
  }
1329
1328
  /**
1330
- * Fire is a light source (§4.8, §7). A fire that changes nothing around it
1329
+ * Fire is a light source. A fire that changes nothing around it
1331
1330
  * looks pasted on.
1332
1331
  *
1333
1332
  * One warm point light for each place the fire is gathered — up to
@@ -1345,7 +1344,7 @@ interface FxFireLightProps {
1345
1344
  *
1346
1345
  * And one dimmer light on the FAR side of the sheet, at the fire as a whole,
1347
1346
  * for translucency: paper is thin, and a fire in front of it glows through to
1348
- * the back (§7). The sheet's own material lights its back face from whatever
1347
+ * the back. The sheet's own material lights its back face from whatever
1349
1348
  * is behind it.
1350
1349
  *
1351
1350
  * Every light is mounted all the time and only its intensity changes, so a
@@ -1355,7 +1354,7 @@ interface FxFireLightProps {
1355
1354
  declare function FxFireLight({ field, locate, gain, shadows }: FxFireLightProps): react.JSX.Element;
1356
1355
  /**
1357
1356
  * Light per unit of front length; the front runs to ~0.045 at a burn's peak.
1358
- * Noor's tune, 2026-09-13 (it was 42).
1357
+ * Tuned in `/fx-lab` (it was 42).
1359
1358
  */
1360
1359
  declare const FIRE_LIGHT_GAIN = 26;
1361
1360
 
@@ -1392,8 +1391,7 @@ interface FxMatchFlameProps {
1392
1391
  };
1393
1392
  }
1394
1393
  /**
1395
- * A match, held: `paperlab-fx-fire-spec.md` §10, the parts that are the
1396
- * flame's to draw.
1394
+ * A match, held: the parts of it that are the flame's to draw.
1397
1395
  *
1398
1396
  * - **Arming.** Friction building: a few tiny sparks and a warm point that
1399
1397
  * grows at the pinch. A pinch released early (a flick) fizzles — nothing
@@ -1407,7 +1405,7 @@ interface FxMatchFlameProps {
1407
1405
  * - **Blown.** It leans away and flickers hard; when the page says it has
1408
1406
  * gone out, it leaves a soft puff behind.
1409
1407
  * - **Light.** It lights the sheet warm before anything scorches — bringing
1410
- * the flame near the paper is the first thing people notice (§10.4).
1408
+ * the flame near the paper is the first thing people notice.
1411
1409
  *
1412
1410
  * It owns its own clock and its own handful of particles: nothing here is
1413
1411
  * part of a burn anyone replays.
@@ -1417,7 +1415,7 @@ declare function FxMatchFlame({ match }: FxMatchFlameProps): react.JSX.Element;
1417
1415
  /**
1418
1416
  * Where a burn's flames stand, read off the field.
1419
1417
  *
1420
- * `paperlab-fx-fire-spec.md` §4.1: paper does not burn, the gas does. Heat
1418
+ * Paper does not burn, the gas does. Heat
1421
1419
  * cooks the cellulose into gas, which burns just ABOVE the surface, rooted
1422
1420
  * over the char right behind the ember line — so a flame belongs on the edge
1423
1421
  * of the hole, where it is hot, and nowhere else. Where the heat has gone
@@ -1435,7 +1433,7 @@ interface FlameAnchor {
1435
1433
  x: number;
1436
1434
  y: number;
1437
1435
  z: number;
1438
- /** World units. 10–40 mm at A4 (§6), scaled by heat and by which rim it is on. */
1436
+ /** World units. 10–40 mm at A4, scaled by heat and by which rim it is on. */
1439
1437
  height: number;
1440
1438
  width: number;
1441
1439
  /** Stable while the cell burns, so a flame keeps its own flicker frame to frame. */
@@ -1472,7 +1470,7 @@ declare function flameAnchors(field: DamageField, locate: SurfaceLocator, max: n
1472
1470
  /**
1473
1471
  * How much a flame has puffed up at a moment — the same function the flame
1474
1472
  * shader runs, so the fire light flickers WITH the flames rather than beside
1475
- * them (§7). Value noise, 10–15 Hz, seeded per flame: neighbours never move
1473
+ * them. Value noise, 10–15 Hz, seeded per flame: neighbours never move
1476
1474
  * in step, and nothing is a sine.
1477
1475
  */
1478
1476
  declare function flamePuff(seed: number, time: number): number;
@@ -1795,13 +1793,13 @@ declare class FireSound {
1795
1793
  update(dt: number, stats: FieldStats, at?: SoundAt | null): void;
1796
1794
  /**
1797
1795
  * A match struck: the scratch of the head across the box, then the hiss of
1798
- * it flaring as the head burns off (spec §10.2). Two bursts of the shared
1796
+ * it flaring as the head burns off. Two bursts of the shared
1799
1797
  * noise, one bright and short, one breathier and longer.
1800
1798
  */
1801
1799
  strike(): void;
1802
- /** Blown out: a soft, low breath of noise (spec §10.6). */
1800
+ /** Blown out: a soft, low breath of noise. */
1803
1801
  puff(): void;
1804
- /** One ember popping in the air — a tiny click on the frame it flashes (spec §8.1). */
1802
+ /** One ember popping in the air — a tiny click on the frame it flashes. */
1805
1803
  pop(): void;
1806
1804
  /** Silence, now — the flame blown out, or the page going away. */
1807
1805
  stop(): void;
package/dist/fx.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { D as DamageSource } from './damageContract-nF1fgK7P.js';
2
- export { a as DAMAGE_CHANNELS, b as DAMAGE_LOOK_DEFAULTS, c as DamageLook } from './damageContract-nF1fgK7P.js';
1
+ import { D as DamageSource } from './damageContract-9RC4egJg.js';
2
+ export { a as DAMAGE_CHANNELS, b as DAMAGE_LOOK_DEFAULTS, c as DamageLook } from './damageContract-9RC4egJg.js';
3
3
  import * as react from 'react';
4
4
  import { ReactNode } from 'react';
5
5
  import * as THREE from 'three';
@@ -366,7 +366,7 @@ declare const particlePresets: {
366
366
  * caught the bug in the version before this one. That version said "the hot
367
367
  * end is now ~5.6, well over the bloom threshold" — and 5.6 was an ABSOLUTE
368
368
  * luminance, while paper white under `window` is 1.6. So the hottest spark
369
- * in the frame was **3.5× paper, under the spec's own 4× floor**, and its
369
+ * in the frame was **3.5× paper, under the 4× floor**, and its
370
370
  * own comment said otherwise. A number that cannot be compared to anything
371
371
  * is a number nobody can check.
372
372
  *
@@ -540,8 +540,8 @@ interface FireEmitterOptions {
540
540
  };
541
541
  }
542
542
  /**
543
- * How much each kind leaves the front, per texel per second — Noor's tune in
544
- * the lab on 2026-09-13, which the lab starts from and `/hands` inherits.
543
+ * How much each kind leaves the front, per texel per second — the tune made in
544
+ * the lab, which the lab starts from and `/hands` inherits.
545
545
  *
546
546
  * The simulator's smoke is the main smoke of a burning sheet, since it is the
547
547
  * same fluid the flames are made of; these sprite puffs, once switched off
@@ -576,8 +576,8 @@ declare class FireEmitter {
576
576
  }
577
577
 
578
578
  /**
579
- * The pool, drawn — three kinds, three ways, because the spec's three
580
- * particles are three different things (paperlab-fx-fire-spec.md §8).
579
+ * The pool, drawn — three kinds, three ways, because a fire's three
580
+ * particles are three different things.
581
581
  *
582
582
  * - **Embers** are light, moving fast: quads STRETCHED along their own
583
583
  * screen-space velocity into streaks, a bright head and a fading tail,
@@ -655,21 +655,21 @@ interface FxQualitySettings {
655
655
  * Bloom is a blur, and a blur at half resolution is a quarter of the
656
656
  * fill-rate for a glow that is soft anyway — the cheapest thing a phone
657
657
  * can give up that still leaves every zone and every flame on screen
658
- * (spec §12: tiers thin post quality, never what is shown). Read by
658
+ * (tiers thin post quality, never what is shown). Read by
659
659
  * `FxPost`.
660
660
  */
661
661
  bloomScale: number;
662
662
  /**
663
- * The most flames standing at once. The spec's §12 said 8 / 16 / 32; a ring
663
+ * The most flames standing at once. It was 8 / 16 / 32; a ring
664
664
  * of fire that is dense in clusters and broken by gaps needs more tongues
665
- * than that, and Noor's direction (a living, uneven ring, never a crown)
666
- * came later and wins — 16 / 36 / 64. Every tier has real flames; a phone
665
+ * than that (a living, uneven ring, never a crown), so it is
666
+ * 16 / 36 / 64. Every tier has real flames; a phone
667
667
  * gets fewer tongues, never the cheap version.
668
668
  * Read by `FxFlames`.
669
669
  */
670
670
  flames: number;
671
671
  /**
672
- * The most of each particle kind in the air at once (spec §12), inside the
672
+ * The most of each particle kind in the air at once, inside the
673
673
  * `particles` budget. Read by `FireEmitter` through its `caps` option.
674
674
  */
675
675
  caps: {
@@ -681,7 +681,7 @@ interface FxQualitySettings {
681
681
  * Heat haze strength in pixels at 1080p; 0 turns it off. Read by `FxPost`.
682
682
  *
683
683
  * High tier only, and half what it was. It was 3.8 px on two tiers against
684
- * §10's 1–3, and invisible in every capture at either — a few pixels of
684
+ * the 1–3 wanted, and invisible in every capture at either — a few pixels of
685
685
  * wobble reads as nothing while the fluid beside it is moving. It is also
686
686
  * still driven from `flameAnchors`, which is where the SPRITE flames stand,
687
687
  * not where the fluid actually burns; until it reads the fluid's own heat
@@ -731,7 +731,7 @@ interface FxPostProps {
731
731
  threshold?: number;
732
732
  /**
733
733
  * The burning field, and where its sheet is — for the heat haze above its
734
- * flames and the frame's warm grade as it grows (§7). Leave both out and
734
+ * flames and the frame's warm grade as it grows. Leave both out and
735
735
  * the pass is bloom and the tone curve alone.
736
736
  */
737
737
  field?: DamageField;
@@ -762,7 +762,7 @@ declare function FxPost(props: FxPostProps): react.JSX.Element;
762
762
  * were identical: most presets are safe from 0.8–0.9, `studio` from 1.1, and
763
763
  * `window` — the brightest — only from 1.6. The brightest preset has to set
764
764
  * this, because a threshold that lets paper bloom under any preset brings back
765
- * the painted-glow failure of §0.
765
+ * the painted-glow failure of the first fire.
766
766
  *
767
767
  * Re-measure it with `pnpm test:fire-look` if a preset's lighting changes.
768
768
  */
@@ -775,14 +775,14 @@ declare const PAPER_WHITE = 1.6;
775
775
  * the number survives: it is not "3.6", it is "clear of the brightest paper".
776
776
  *
777
777
  * **Do not lower it to make fire bloom.** That is the wrong end of the
778
- * problem, and the review that found the fluid's flames under it said so:
778
+ * problem, and a visual review that found the fluid's flames under it said so:
779
779
  * `window`'s paper blooms from 1.6, so lowering the threshold blooms paper,
780
780
  * which is the painted glow again. Raise what the fire EMITS.
781
781
  */
782
782
  declare const FX_BLOOM_THRESHOLD: number;
783
783
  /**
784
784
  * What the spec asks a glowing part of a fire to reach, in multiples of paper
785
- * white (§4.3). The band `emission.test.ts` holds every emitter to.
785
+ * white. The band `emission.test.ts` holds every emitter to.
786
786
  */
787
787
  declare const FIRE_GLOW: readonly [4, 8];
788
788
  /** One sRGB channel (0..1) to linear. The transfer function, not a gamma. */
@@ -845,8 +845,8 @@ declare const FIRE_SOOT_SCALE = 3;
845
845
  * multiplier on whatever clears the threshold, so its right value depends
846
846
  * entirely on how much fire is authored above it. With a core authored to
847
847
  * over-expose, 0.55 was the most the black stage could take; the flame of
848
- * {@link FIRE_ZONES} (Noor, 2026-09-13) authors none of it past the
849
- * threshold, and 1.25 is her tune for it. `pnpm test:fire-budget` holds the
848
+ * {@link FIRE_ZONES} authors none of it past the
849
+ * threshold, and 1.25 is the tune for it. `pnpm test:fire-budget` holds the
850
850
  * stage black and the paper unbloomed whatever this is set to.
851
851
  */
852
852
  declare const FX_BLOOM = 1.25;
@@ -882,7 +882,7 @@ declare const FX_BLOOM = 1.25;
882
882
  * both ways. Above it, a flame keeps its light in a bright room but loses its
883
883
  * colour to the tone curve; below it, the colour holds, and a flame drawn
884
884
  * opaque risks reading as a decal on the sheet rather than as light. The
885
- * defaults sit below it (Noor, 2026-09-13), with the bloom carrying the glow.
885
+ * defaults sit below it, with the bloom carrying the glow.
886
886
  */
887
887
  interface FireZones {
888
888
  root: {
@@ -913,7 +913,7 @@ type FireZonesInput = {
913
913
  [Z in keyof FireZones]?: Partial<FireZones[Z]>;
914
914
  };
915
915
  /**
916
- * Noor's tune in the lab on 2026-09-13, and the source of truth for how a
916
+ * The tune made in `/fx-lab`, and the source of truth for how a
917
917
  * flame looks: the lab starts from it and `/hands` inherits it. A gentler
918
918
  * flame than the one it replaced — every zone sits under the bloom threshold,
919
919
  * so its glow comes from the bloom's strength ({@link FX_BLOOM}) spreading
@@ -938,9 +938,9 @@ interface AfterglowOptions {
938
938
  /**
939
939
  * The embers a burn leaves behind, drawn over the field it came from.
940
940
  *
941
- * `paperlab-fx-fire-spec.md` §9, "Smoulder": after the flames, glowing spots
941
+ * Smoulder: after the flames, glowing spots
942
942
  * crawl along the edge, flare on a breath, and go out one by one over
943
- * seconds (§4.10). The field cannot do this, and it is not a tuning problem:
943
+ * seconds. The field cannot do this, and it is not a tuning problem:
944
944
  * a burn blown out loses its heat in a fraction of a second — below ignition
945
945
  * everywhere or it recovers — so its own heat channel has nothing left to
946
946
  * smoulder with.
@@ -954,7 +954,7 @@ interface AfterglowOptions {
954
954
  * breath flares what is left.
955
955
  *
956
956
  * Only the sheet reads it. Flames and the fire light read the FIELD, so the
957
- * flames die with the gas (§4.1) while the edge keeps glowing; the physics
957
+ * flames die with the gas while the edge keeps glowing; the physics
958
958
  * reads char, saturation and presence, which pass through untouched.
959
959
  */
960
960
  declare class Afterglow implements DamageSource {
@@ -977,7 +977,7 @@ declare class Afterglow implements DamageSource {
977
977
  get smouldering(): boolean;
978
978
  /**
979
979
  * Advance by `dt`, after the field has stepped. `blow` is the breath on the
980
- * sheet, 0..1: it flares what is left (§10.6, "on smoulder: beads flare,
980
+ * sheet, 0..1: it flares what is left ("on smoulder: beads flare,
981
981
  * then fade").
982
982
  */
983
983
  step(dt: number, blow?: number): void;
@@ -994,7 +994,7 @@ interface FxFlamesProps {
994
994
  quality?: FxQualityTier;
995
995
  /**
996
996
  * The air, world units a second — pass the particle pool's `wind`, which is
997
- * read every frame. Flames lean away from it and shorten (§10.6).
997
+ * read every frame. Flames lean away from it and shorten.
998
998
  */
999
999
  wind?: readonly [number, number, number];
1000
1000
  }
@@ -1002,8 +1002,8 @@ interface FxFlamesProps {
1002
1002
  * The flames of a burn: separate tongues of burning gas, standing just off
1003
1003
  * the char, rising straight up whatever the sheet is doing.
1004
1004
  *
1005
- * `paperlab-fx-fire-spec.md` §6. Each tongue is a quad that turns to face the
1006
- * camera about WORLD up — never the sheet's normal (§13.9) — so a flame on a
1005
+ * Each tongue is a quad that turns to face the
1006
+ * camera about WORLD up — never the sheet's normal — so a flame on a
1007
1007
  * sheet held flat still rises to the ceiling. The shader is upward-scrolling,
1008
1008
  * domain-warped noise inside a teardrop, coloured on the flame's own
1009
1009
  * blackbody ramp: a dim indigo root, a yellow-white core, orange edges and
@@ -1042,8 +1042,8 @@ declare function FxWisps({ glow, field, locate, wind }: FxWispsProps): react.JSX
1042
1042
 
1043
1043
  /**
1044
1044
  * The fire simulator's controls — by the names, and at the defaults, of the
1045
- * fluid-fire tool Noor pointed at (Emission, Combustion, Fuel & air, Motion
1046
- * & turbulence), so the lab's panel reads like the one she knows.
1045
+ * fluid-fire tool it was modelled on (Emission, Combustion, Fuel & air, Motion
1046
+ * & turbulence), so the lab's panel reads like that one.
1047
1047
  *
1048
1048
  * The numbers are the panel's, not the solver's. `solverUniforms` is the one
1049
1049
  * place they are turned into this solver's units — world units (a default
@@ -1102,8 +1102,7 @@ interface FireFluidParams {
1102
1102
  smokeAfter: number;
1103
1103
  }
1104
1104
  /**
1105
- * The defaults — the panel's names, at the values Noor tuned in the lab on
1106
- * 2026-09-12. No turbulence of its own: the motion comes from vorticity,
1105
+ * The defaults — the panel's names, at the values tuned in the lab. No turbulence of its own: the motion comes from vorticity,
1107
1106
  * the radial impulse and the uneven rim that feeds it.
1108
1107
  */
1109
1108
  declare const fireFluidDefaults: FireFluidParams;
@@ -1327,7 +1326,7 @@ interface FxFireLightProps {
1327
1326
  shadows?: boolean;
1328
1327
  }
1329
1328
  /**
1330
- * Fire is a light source (§4.8, §7). A fire that changes nothing around it
1329
+ * Fire is a light source. A fire that changes nothing around it
1331
1330
  * looks pasted on.
1332
1331
  *
1333
1332
  * One warm point light for each place the fire is gathered — up to
@@ -1345,7 +1344,7 @@ interface FxFireLightProps {
1345
1344
  *
1346
1345
  * And one dimmer light on the FAR side of the sheet, at the fire as a whole,
1347
1346
  * for translucency: paper is thin, and a fire in front of it glows through to
1348
- * the back (§7). The sheet's own material lights its back face from whatever
1347
+ * the back. The sheet's own material lights its back face from whatever
1349
1348
  * is behind it.
1350
1349
  *
1351
1350
  * Every light is mounted all the time and only its intensity changes, so a
@@ -1355,7 +1354,7 @@ interface FxFireLightProps {
1355
1354
  declare function FxFireLight({ field, locate, gain, shadows }: FxFireLightProps): react.JSX.Element;
1356
1355
  /**
1357
1356
  * Light per unit of front length; the front runs to ~0.045 at a burn's peak.
1358
- * Noor's tune, 2026-09-13 (it was 42).
1357
+ * Tuned in `/fx-lab` (it was 42).
1359
1358
  */
1360
1359
  declare const FIRE_LIGHT_GAIN = 26;
1361
1360
 
@@ -1392,8 +1391,7 @@ interface FxMatchFlameProps {
1392
1391
  };
1393
1392
  }
1394
1393
  /**
1395
- * A match, held: `paperlab-fx-fire-spec.md` §10, the parts that are the
1396
- * flame's to draw.
1394
+ * A match, held: the parts of it that are the flame's to draw.
1397
1395
  *
1398
1396
  * - **Arming.** Friction building: a few tiny sparks and a warm point that
1399
1397
  * grows at the pinch. A pinch released early (a flick) fizzles — nothing
@@ -1407,7 +1405,7 @@ interface FxMatchFlameProps {
1407
1405
  * - **Blown.** It leans away and flickers hard; when the page says it has
1408
1406
  * gone out, it leaves a soft puff behind.
1409
1407
  * - **Light.** It lights the sheet warm before anything scorches — bringing
1410
- * the flame near the paper is the first thing people notice (§10.4).
1408
+ * the flame near the paper is the first thing people notice.
1411
1409
  *
1412
1410
  * It owns its own clock and its own handful of particles: nothing here is
1413
1411
  * part of a burn anyone replays.
@@ -1417,7 +1415,7 @@ declare function FxMatchFlame({ match }: FxMatchFlameProps): react.JSX.Element;
1417
1415
  /**
1418
1416
  * Where a burn's flames stand, read off the field.
1419
1417
  *
1420
- * `paperlab-fx-fire-spec.md` §4.1: paper does not burn, the gas does. Heat
1418
+ * Paper does not burn, the gas does. Heat
1421
1419
  * cooks the cellulose into gas, which burns just ABOVE the surface, rooted
1422
1420
  * over the char right behind the ember line — so a flame belongs on the edge
1423
1421
  * of the hole, where it is hot, and nowhere else. Where the heat has gone
@@ -1435,7 +1433,7 @@ interface FlameAnchor {
1435
1433
  x: number;
1436
1434
  y: number;
1437
1435
  z: number;
1438
- /** World units. 10–40 mm at A4 (§6), scaled by heat and by which rim it is on. */
1436
+ /** World units. 10–40 mm at A4, scaled by heat and by which rim it is on. */
1439
1437
  height: number;
1440
1438
  width: number;
1441
1439
  /** Stable while the cell burns, so a flame keeps its own flicker frame to frame. */
@@ -1472,7 +1470,7 @@ declare function flameAnchors(field: DamageField, locate: SurfaceLocator, max: n
1472
1470
  /**
1473
1471
  * How much a flame has puffed up at a moment — the same function the flame
1474
1472
  * shader runs, so the fire light flickers WITH the flames rather than beside
1475
- * them (§7). Value noise, 10–15 Hz, seeded per flame: neighbours never move
1473
+ * them. Value noise, 10–15 Hz, seeded per flame: neighbours never move
1476
1474
  * in step, and nothing is a sine.
1477
1475
  */
1478
1476
  declare function flamePuff(seed: number, time: number): number;
@@ -1795,13 +1793,13 @@ declare class FireSound {
1795
1793
  update(dt: number, stats: FieldStats, at?: SoundAt | null): void;
1796
1794
  /**
1797
1795
  * A match struck: the scratch of the head across the box, then the hiss of
1798
- * it flaring as the head burns off (spec §10.2). Two bursts of the shared
1796
+ * it flaring as the head burns off. Two bursts of the shared
1799
1797
  * noise, one bright and short, one breathier and longer.
1800
1798
  */
1801
1799
  strike(): void;
1802
- /** Blown out: a soft, low breath of noise (spec §10.6). */
1800
+ /** Blown out: a soft, low breath of noise. */
1803
1801
  puff(): void;
1804
- /** One ember popping in the air — a tiny click on the frame it flashes (spec §8.1). */
1802
+ /** One ember popping in the air — a tiny click on the frame it flashes. */
1805
1803
  pop(): void;
1806
1804
  /** Silence, now — the flame blown out, or the page going away. */
1807
1805
  stop(): void;