paperlab 0.7.1 → 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 (37) hide show
  1. package/README.md +57 -28
  2. package/dist/{FxPostPass-M66QMVVC.js → FxPostPass-ACFSHY3S.js} +24 -6
  3. package/dist/FxPostPass-ACFSHY3S.js.map +1 -0
  4. package/dist/chunk-BIFUJCUA.js +34 -0
  5. package/dist/chunk-BIFUJCUA.js.map +1 -0
  6. package/dist/{chunk-NTDVOOBA.js → chunk-LUKNPQ3S.js} +385 -165
  7. package/dist/chunk-LUKNPQ3S.js.map +1 -0
  8. package/dist/{chunk-GB6BMHC3.js → chunk-S74YZAXE.js} +7 -19
  9. package/dist/chunk-S74YZAXE.js.map +1 -0
  10. package/dist/{damageContract-DPF1YFLZ.d.cts → damageContract-9RC4egJg.d.cts} +31 -3
  11. package/dist/{damageContract-DPF1YFLZ.d.ts → damageContract-9RC4egJg.d.ts} +31 -3
  12. package/dist/fx.cjs +336 -107
  13. package/dist/fx.cjs.map +1 -1
  14. package/dist/fx.d.cts +113 -89
  15. package/dist/fx.d.ts +113 -89
  16. package/dist/fx.js +293 -63
  17. package/dist/fx.js.map +1 -1
  18. package/dist/index.cjs +422 -193
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +13 -5
  21. package/dist/index.d.ts +13 -5
  22. package/dist/index.js +21 -8
  23. package/dist/index.js.map +1 -1
  24. package/dist/{slots-C5PqY0Q5.d.cts → slots-DbJQfwVd.d.cts} +13 -1
  25. package/dist/{slots-C5PqY0Q5.d.ts → slots-DbJQfwVd.d.ts} +13 -1
  26. package/dist/stage.cjs +417 -201
  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-M66QMVVC.js.map +0 -1
  34. package/dist/chunk-7TLIWPGM.js +0 -36
  35. package/dist/chunk-7TLIWPGM.js.map +0 -1
  36. package/dist/chunk-GB6BMHC3.js.map +0 -1
  37. package/dist/chunk-NTDVOOBA.js.map +0 -1
package/dist/fx.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { D as DamageSource } from './damageContract-DPF1YFLZ.cjs';
2
- export { a as DAMAGE_CHANNELS, b as DAMAGE_LOOK_DEFAULTS, c as DamageLook } from './damageContract-DPF1YFLZ.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
  *
@@ -400,7 +400,7 @@ declare const particlePresets: {
400
400
  * simulation, on purpose; see the fx plan.
401
401
  */
402
402
  readonly smoke: {
403
- readonly life: readonly [1.8, 3.5];
403
+ readonly life: readonly [1.4, 2.6];
404
404
  readonly speed: readonly [0.05, 0.15];
405
405
  readonly direction: readonly [0, 1, 0];
406
406
  readonly spread: 0.3;
@@ -408,9 +408,9 @@ declare const particlePresets: {
408
408
  readonly drag: 0.9;
409
409
  readonly jitter: 0.3;
410
410
  readonly windCatch: 1;
411
- readonly size: readonly [0.05, 0.35];
411
+ readonly size: readonly [0.05, 0.18];
412
412
  readonly color: readonly [readonly [0.147, 0.13, 0.117], readonly [0.2, 0.19, 0.18]];
413
- readonly alpha: readonly [0.08, 0];
413
+ readonly alpha: readonly [0.07, 0];
414
414
  readonly spin: 0.4;
415
415
  readonly blend: "normal";
416
416
  readonly shape: "soft";
@@ -422,16 +422,16 @@ declare const particlePresets: {
422
422
  * burn that proves paper was there.
423
423
  */
424
424
  readonly ash: {
425
- readonly life: readonly [2.5, 5];
425
+ readonly life: readonly [1.2, 2.6];
426
426
  readonly speed: readonly [0.14, 0.3];
427
427
  readonly direction: readonly [0, 1, 0];
428
428
  readonly spread: 0.55;
429
- readonly lift: -0.22;
429
+ readonly lift: -0.42;
430
430
  readonly drag: 1.6;
431
431
  readonly jitter: 1.2;
432
432
  readonly windCatch: 1;
433
433
  readonly size: readonly [0.03, 0.026];
434
- readonly color: readonly [readonly [0.07, 0.065, 0.06], readonly [0.12, 0.115, 0.11]];
434
+ readonly color: readonly [readonly [0.04, 0.037, 0.034], readonly [0.07, 0.066, 0.062]];
435
435
  readonly alpha: readonly [0.95, 0];
436
436
  readonly spin: 4;
437
437
  readonly blend: "normal";
@@ -540,19 +540,13 @@ interface FireEmitterOptions {
540
540
  };
541
541
  }
542
542
  /**
543
- * How much each kind leaves the front, per texel per second.
544
- *
545
- * `smoke` is 0 on purpose. There were three smoke systems running at once —
546
- * the fire simulator's own, these sprite puffs at 8% alpha, and the smoulder
547
- * wisp and the review's note was that only one of them should exist. The
548
- * simulator's is the one that belongs to a burning sheet: it is the same
549
- * fluid the flames are made of, so it rises with them instead of beside them.
550
- * `FxWisps` still carries the thread after the flames are out, which is the
551
- * one moment the simulator has nothing left to make. The preset stays: it is
552
- * a parameter set, and anything may still ask the pool for smoke.
553
- *
554
- * Embers and ash are both down. Ash at 0.86 a texel was a flake for nearly
555
- * every one that burnt through — a dust shower. Fewer and larger is the note.
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
+ *
546
+ * The simulator's smoke is the main smoke of a burning sheet, since it is the
547
+ * same fluid the flames are made of; these sprite puffs, once switched off
548
+ * for doubling it, are back as a light thread beside it. Ash at 0.86 a texel
549
+ * was a dust shower; 0.36 is a light fall of it.
556
550
  */
557
551
  declare const fireEmitterDefaults: Required<FireEmitterOptions>;
558
552
  /**
@@ -582,8 +576,8 @@ declare class FireEmitter {
582
576
  }
583
577
 
584
578
  /**
585
- * The pool, drawn — three kinds, three ways, because the spec's three
586
- * 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.
587
581
  *
588
582
  * - **Embers** are light, moving fast: quads STRETCHED along their own
589
583
  * screen-space velocity into streaks, a bright head and a fading tail,
@@ -661,21 +655,21 @@ interface FxQualitySettings {
661
655
  * Bloom is a blur, and a blur at half resolution is a quarter of the
662
656
  * fill-rate for a glow that is soft anyway — the cheapest thing a phone
663
657
  * can give up that still leaves every zone and every flame on screen
664
- * (spec §12: tiers thin post quality, never what is shown). Read by
658
+ * (tiers thin post quality, never what is shown). Read by
665
659
  * `FxPost`.
666
660
  */
667
661
  bloomScale: number;
668
662
  /**
669
- * 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
670
664
  * of fire that is dense in clusters and broken by gaps needs more tongues
671
- * than that, and Noor's direction (a living, uneven ring, never a crown)
672
- * 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
673
667
  * gets fewer tongues, never the cheap version.
674
668
  * Read by `FxFlames`.
675
669
  */
676
670
  flames: number;
677
671
  /**
678
- * 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
679
673
  * `particles` budget. Read by `FireEmitter` through its `caps` option.
680
674
  */
681
675
  caps: {
@@ -687,7 +681,7 @@ interface FxQualitySettings {
687
681
  * Heat haze strength in pixels at 1080p; 0 turns it off. Read by `FxPost`.
688
682
  *
689
683
  * High tier only, and half what it was. It was 3.8 px on two tiers against
690
- * §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
691
685
  * wobble reads as nothing while the fluid beside it is moving. It is also
692
686
  * still driven from `flameAnchors`, which is where the SPRITE flames stand,
693
687
  * not where the fluid actually burns; until it reads the fluid's own heat
@@ -737,13 +731,22 @@ interface FxPostProps {
737
731
  threshold?: number;
738
732
  /**
739
733
  * The burning field, and where its sheet is — for the heat haze above its
740
- * 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
741
735
  * the pass is bloom and the tone curve alone.
742
736
  */
743
737
  field?: DamageField;
744
738
  locate?: SurfaceLocator;
745
739
  /** Heat haze in pixels at 1080p, overriding the tier's; 0 turns it off. */
746
740
  haze?: number;
741
+ /**
742
+ * Shallow focus on the burn, 0..1; 0 keeps everything sharp.
743
+ *
744
+ * A macro lens held close to a burning edge has a few millimetres of
745
+ * focus and nothing else (§K4): the rim is sharp, the rest of the sheet
746
+ * goes soft. It focuses on the fire itself — the burning rim is what the
747
+ * shot is about — so it needs `field` and `locate` like the haze does.
748
+ */
749
+ focus?: number;
747
750
  }
748
751
  /**
749
752
  * `fallback={null}`: the scene behind it is already drawn. One frame without
@@ -759,7 +762,7 @@ declare function FxPost(props: FxPostProps): react.JSX.Element;
759
762
  * were identical: most presets are safe from 0.8–0.9, `studio` from 1.1, and
760
763
  * `window` — the brightest — only from 1.6. The brightest preset has to set
761
764
  * this, because a threshold that lets paper bloom under any preset brings back
762
- * the painted-glow failure of §0.
765
+ * the painted-glow failure of the first fire.
763
766
  *
764
767
  * Re-measure it with `pnpm test:fire-look` if a preset's lighting changes.
765
768
  */
@@ -772,14 +775,14 @@ declare const PAPER_WHITE = 1.6;
772
775
  * the number survives: it is not "3.6", it is "clear of the brightest paper".
773
776
  *
774
777
  * **Do not lower it to make fire bloom.** That is the wrong end of the
775
- * 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:
776
779
  * `window`'s paper blooms from 1.6, so lowering the threshold blooms paper,
777
780
  * which is the painted glow again. Raise what the fire EMITS.
778
781
  */
779
782
  declare const FX_BLOOM_THRESHOLD: number;
780
783
  /**
781
784
  * What the spec asks a glowing part of a fire to reach, in multiples of paper
782
- * white (§4.3). The band `emission.test.ts` holds every emitter to.
785
+ * white. The band `emission.test.ts` holds every emitter to.
783
786
  */
784
787
  declare const FIRE_GLOW: readonly [4, 8];
785
788
  /** One sRGB channel (0..1) to linear. The transfer function, not a gamma. */
@@ -840,13 +843,13 @@ declare const FIRE_SOOT_SCALE = 3;
840
843
  *
841
844
  * It lives beside the threshold because the two are one setting: bloom is a
842
845
  * multiplier on whatever clears the threshold, so its right value depends
843
- * entirely on how much fire is authored above it. 1.55 was tuned when the
844
- * flames were UNDER the threshold and bloom had almost nothing to work on —
845
- * measured, 0.07% of the frame. Once the flames cleared it, that same 1.55
846
- * tinted the entire black stage olive. Swept at 0.25, 0.6 and 1.2 against the
847
- * peak frame; past about 0.4 the stage stops being black.
846
+ * entirely on how much fire is authored above it. With a core authored to
847
+ * over-expose, 0.55 was the most the black stage could take; the flame of
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
+ * stage black and the paper unbloomed whatever this is set to.
848
851
  */
849
- declare const FX_BLOOM = 0.55;
852
+ declare const FX_BLOOM = 1.25;
850
853
  /**
851
854
  * A flame, in four zones — drawn the way the sheet's burn is (ember line, ash
852
855
  * lip, char, scorch): each part named for what it is, with its own controls.
@@ -854,11 +857,10 @@ declare const FX_BLOOM = 0.55;
854
857
  *
855
858
  * root the blue leading edge where fresh gas meets the air at the paper.
856
859
  * Its light comes from excited molecules, not soot, so it is faint
857
- * and blue — and it starts OFF, because blue light added over cream
858
- * paper reads lavender.
859
- * core the hottest gas, where soot forms densest and glows pale. The only
860
- * zone allowed to over-expose past the bloom threshold, and so the
861
- * one that blooms.
860
+ * and blue — and only ever a trace, because blue light added over
861
+ * cream paper reads lavender.
862
+ * core the hottest gas, where soot forms densest and glows pale the
863
+ * brightest zone, and the one the bloom finds first.
862
864
  * body the luminous bulk: soot glowing yellow-orange as it rises.
863
865
  * tip where soot cools and burns off at the outer edge — orange-red,
864
866
  * dimmer, tearing into tongues. What survives escapes as smoke.
@@ -876,12 +878,11 @@ declare const FX_BLOOM = 0.55;
876
878
  * multiples of paper white; colours are sRGB, the way a colour picker gives
877
879
  * them.
878
880
  *
879
- * The body is ABOVE paper white. It was once held below it, because under a
880
- * bright preset paper sits at the top of the tone curve and anything brighter
881
- * loses its colour and a flame dimmer than the paper behind it, drawn
882
- * opaque, is a yellow decal on the sheet, not light. Fire photographed in a
883
- * bright room DOES wash out; that is the lighting's to answer (the lab shoots
884
- * under `noir`, as the references were), not the flame's.
881
+ * How bright the body is against paper white is a judgement, and it has gone
882
+ * both ways. Above it, a flame keeps its light in a bright room but loses its
883
+ * colour to the tone curve; below it, the colour holds, and a flame drawn
884
+ * opaque risks reading as a decal on the sheet rather than as light. The
885
+ * defaults sit below it, with the bloom carrying the glow.
885
886
  */
886
887
  interface FireZones {
887
888
  root: {
@@ -912,10 +913,12 @@ type FireZonesInput = {
912
913
  [Z in keyof FireZones]?: Partial<FireZones[Z]>;
913
914
  };
914
915
  /**
915
- * The defaults reproduce the look arrived at by measurement against
916
- * Flame_base.png (yellow ~30%, orange ~40%, pale ~18% of a flame's pixels),
917
- * now expressed zone by zone. Each colour is the linear emission the previous
918
- * ramp used in that band, written as the sRGB a picker would show.
916
+ * The tune made in `/fx-lab`, and the source of truth for how a
917
+ * flame looks: the lab starts from it and `/hands` inherits it. A gentler
918
+ * flame than the one it replaced every zone sits under the bloom threshold,
919
+ * so its glow comes from the bloom's strength ({@link FX_BLOOM}) spreading
920
+ * the brightest gas, not from a core authored to over-expose. The values it
921
+ * replaced, measured against Flame_base.png, are in the history of this file.
919
922
  */
920
923
  declare const FIRE_ZONES: FireZones;
921
924
  /** The defaults with `input` laid over them, zone by zone. */
@@ -935,9 +938,9 @@ interface AfterglowOptions {
935
938
  /**
936
939
  * The embers a burn leaves behind, drawn over the field it came from.
937
940
  *
938
- * `paperlab-fx-fire-spec.md` §9, "Smoulder": after the flames, glowing spots
941
+ * Smoulder: after the flames, glowing spots
939
942
  * crawl along the edge, flare on a breath, and go out one by one over
940
- * 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:
941
944
  * a burn blown out loses its heat in a fraction of a second — below ignition
942
945
  * everywhere or it recovers — so its own heat channel has nothing left to
943
946
  * smoulder with.
@@ -951,7 +954,7 @@ interface AfterglowOptions {
951
954
  * breath flares what is left.
952
955
  *
953
956
  * Only the sheet reads it. Flames and the fire light read the FIELD, so the
954
- * 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
955
958
  * reads char, saturation and presence, which pass through untouched.
956
959
  */
957
960
  declare class Afterglow implements DamageSource {
@@ -974,7 +977,7 @@ declare class Afterglow implements DamageSource {
974
977
  get smouldering(): boolean;
975
978
  /**
976
979
  * Advance by `dt`, after the field has stepped. `blow` is the breath on the
977
- * 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,
978
981
  * then fade").
979
982
  */
980
983
  step(dt: number, blow?: number): void;
@@ -991,7 +994,7 @@ interface FxFlamesProps {
991
994
  quality?: FxQualityTier;
992
995
  /**
993
996
  * The air, world units a second — pass the particle pool's `wind`, which is
994
- * read every frame. Flames lean away from it and shorten (§10.6).
997
+ * read every frame. Flames lean away from it and shorten.
995
998
  */
996
999
  wind?: readonly [number, number, number];
997
1000
  }
@@ -999,8 +1002,8 @@ interface FxFlamesProps {
999
1002
  * The flames of a burn: separate tongues of burning gas, standing just off
1000
1003
  * the char, rising straight up whatever the sheet is doing.
1001
1004
  *
1002
- * `paperlab-fx-fire-spec.md` §6. Each tongue is a quad that turns to face the
1003
- * 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
1004
1007
  * sheet held flat still rises to the ceiling. The shader is upward-scrolling,
1005
1008
  * domain-warped noise inside a teardrop, coloured on the flame's own
1006
1009
  * blackbody ramp: a dim indigo root, a yellow-white core, orange edges and
@@ -1039,8 +1042,8 @@ declare function FxWisps({ glow, field, locate, wind }: FxWispsProps): react.JSX
1039
1042
 
1040
1043
  /**
1041
1044
  * The fire simulator's controls — by the names, and at the defaults, of the
1042
- * fluid-fire tool Noor pointed at (Emission, Combustion, Fuel & air, Motion
1043
- * & 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.
1044
1047
  *
1045
1048
  * The numbers are the panel's, not the solver's. `solverUniforms` is the one
1046
1049
  * place they are turned into this solver's units — world units (a default
@@ -1099,8 +1102,7 @@ interface FireFluidParams {
1099
1102
  smokeAfter: number;
1100
1103
  }
1101
1104
  /**
1102
- * The defaults — the panel's names, at the values Noor tuned in the lab on
1103
- * 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,
1104
1106
  * the radial impulse and the uneven rim that feeds it.
1105
1107
  */
1106
1108
  declare const fireFluidDefaults: FireFluidParams;
@@ -1313,25 +1315,48 @@ declare class FireFluid {
1313
1315
  interface FxFireLightProps {
1314
1316
  field: DamageField;
1315
1317
  locate: SurfaceLocator;
1316
- /** How bright the fire is per unit of burning front. Tuned against Hero.png. */
1318
+ /** How bright the fire is per unit of burning front. */
1317
1319
  gain?: number;
1320
+ /**
1321
+ * The brightest cluster's light casts shadows: a curled edge, a lifted
1322
+ * plate and a hanging flap throw flickering shade across the sheet, which
1323
+ * is much of what makes firelight read as firelight. One shadow pass a
1324
+ * frame, so off unless asked for.
1325
+ */
1326
+ shadows?: boolean;
1318
1327
  }
1319
1328
  /**
1320
- * 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
1321
1330
  * looks pasted on.
1322
1331
  *
1323
- * A warm point light about 1900 K at the middle of the burning rim,
1324
- * raised by half a flame, as bright as the front is long and flickering on
1325
- * the very noise the flames puff on, so light and flame agree. It reaches the
1326
- * whole sheet: `decay` is held below the physical 2, because a sheet is a
1327
- * small thing next to a fire and Hero.png warms it to the top edge.
1328
- *
1329
- * And a dimmer twin on the FAR side of the sheet, for translucency: paper is
1330
- * thin, and a fire in front of it glows through to the back, warm and
1331
- * diffused (§7). A second light is the whole trick the sheet's own
1332
- * material already lights its back face from whatever is behind it.
1332
+ * One warm point light for each place the fire is gathered up to
1333
+ * {@link FIRE_CLUSTERS} of them, from {@link fireStateOf} standing at its
1334
+ * cluster raised by half a flame, as bright as its share of the fire and
1335
+ * flickering on its own tongues' puff. A tall tongue on the left lights the
1336
+ * left. It used to be one light at the middle of the whole rim, which lit the
1337
+ * sheet the same wherever the flames actually were.
1338
+ *
1339
+ * Each light's colour follows how hot the paper under its tongues is: the
1340
+ * pale peach of a fire at its height, deepening toward orange as it cools —
1341
+ * never toward red, which on white paper is the pink the spec forbids.
1342
+ * Together they give off what the single light did: the front's length times
1343
+ * `gain`, shared out.
1344
+ *
1345
+ * And one dimmer light on the FAR side of the sheet, at the fire as a whole,
1346
+ * for translucency: paper is thin, and a fire in front of it glows through to
1347
+ * the back. The sheet's own material lights its back face from whatever
1348
+ * is behind it.
1349
+ *
1350
+ * Every light is mounted all the time and only its intensity changes, so a
1351
+ * fire gathering into more places or fewer never makes three recompile a
1352
+ * shader.
1353
+ */
1354
+ declare function FxFireLight({ field, locate, gain, shadows }: FxFireLightProps): react.JSX.Element;
1355
+ /**
1356
+ * Light per unit of front length; the front runs to ~0.045 at a burn's peak.
1357
+ * Tuned in `/fx-lab` (it was 42).
1333
1358
  */
1334
- declare function FxFireLight({ field, locate, gain }: FxFireLightProps): react.JSX.Element;
1359
+ declare const FIRE_LIGHT_GAIN = 26;
1335
1360
 
1336
1361
  /** What the page says about the match this frame. Written in place — never a prop change. */
1337
1362
  interface MatchFlameState {
@@ -1366,8 +1391,7 @@ interface FxMatchFlameProps {
1366
1391
  };
1367
1392
  }
1368
1393
  /**
1369
- * A match, held: `paperlab-fx-fire-spec.md` §10, the parts that are the
1370
- * flame's to draw.
1394
+ * A match, held: the parts of it that are the flame's to draw.
1371
1395
  *
1372
1396
  * - **Arming.** Friction building: a few tiny sparks and a warm point that
1373
1397
  * grows at the pinch. A pinch released early (a flick) fizzles — nothing
@@ -1381,7 +1405,7 @@ interface FxMatchFlameProps {
1381
1405
  * - **Blown.** It leans away and flickers hard; when the page says it has
1382
1406
  * gone out, it leaves a soft puff behind.
1383
1407
  * - **Light.** It lights the sheet warm before anything scorches — bringing
1384
- * 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.
1385
1409
  *
1386
1410
  * It owns its own clock and its own handful of particles: nothing here is
1387
1411
  * part of a burn anyone replays.
@@ -1391,7 +1415,7 @@ declare function FxMatchFlame({ match }: FxMatchFlameProps): react.JSX.Element;
1391
1415
  /**
1392
1416
  * Where a burn's flames stand, read off the field.
1393
1417
  *
1394
- * `paperlab-fx-fire-spec.md` §4.1: paper does not burn, the gas does. Heat
1418
+ * Paper does not burn, the gas does. Heat
1395
1419
  * cooks the cellulose into gas, which burns just ABOVE the surface, rooted
1396
1420
  * over the char right behind the ember line — so a flame belongs on the edge
1397
1421
  * of the hole, where it is hot, and nowhere else. Where the heat has gone
@@ -1409,7 +1433,7 @@ interface FlameAnchor {
1409
1433
  x: number;
1410
1434
  y: number;
1411
1435
  z: number;
1412
- /** 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. */
1413
1437
  height: number;
1414
1438
  width: number;
1415
1439
  /** Stable while the cell burns, so a flame keeps its own flicker frame to frame. */
@@ -1446,7 +1470,7 @@ declare function flameAnchors(field: DamageField, locate: SurfaceLocator, max: n
1446
1470
  /**
1447
1471
  * How much a flame has puffed up at a moment — the same function the flame
1448
1472
  * shader runs, so the fire light flickers WITH the flames rather than beside
1449
- * 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
1450
1474
  * in step, and nothing is a sine.
1451
1475
  */
1452
1476
  declare function flamePuff(seed: number, time: number): number;
@@ -1769,13 +1793,13 @@ declare class FireSound {
1769
1793
  update(dt: number, stats: FieldStats, at?: SoundAt | null): void;
1770
1794
  /**
1771
1795
  * A match struck: the scratch of the head across the box, then the hiss of
1772
- * 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
1773
1797
  * noise, one bright and short, one breathier and longer.
1774
1798
  */
1775
1799
  strike(): void;
1776
- /** Blown out: a soft, low breath of noise (spec §10.6). */
1800
+ /** Blown out: a soft, low breath of noise. */
1777
1801
  puff(): void;
1778
- /** 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. */
1779
1803
  pop(): void;
1780
1804
  /** Silence, now — the flame blown out, or the page going away. */
1781
1805
  stop(): void;
@@ -1793,4 +1817,4 @@ declare class FireSound {
1793
1817
  private next;
1794
1818
  }
1795
1819
 
1796
- export { Afterglow, type AfterglowOptions, type AudioLike, type BiquadFilterLike, CHAR, DamageField, type DamageFieldOptions, DamageSource, FIELD_SIZE, FIRE_GLOW, FIRE_HEAT_SCALE, FIRE_SOOT_SCALE, FIRE_ZONES, FIXED_DT, FLAME_HEIGHT, FX_BLOOM, FX_BLOOM_THRESHOLD, FX_INITIAL_TIER, FX_TIER_ORDER, type FieldStats, FireEmitter, type FireEmitterOptions, FireFluid, type FireFluidParams, FireSound, type FireSoundOptions, type FireZones, type FireZonesInput, type FlameAnchor, type FluidGrid, FxAudio, type FxAudioOptions, type FxFilm, FxFireFluid, type FxFireFluidProps, FxFireLight, type FxFireLightProps, FxFlames, type FxFlamesProps, FxMatchFlame, type FxMatchFlameProps, FxParticles, type FxParticlesProps, FxPost, type FxPostProps, type FxQualityName, type FxQualitySettings, type FxQualityTier, FxWisps, type FxWispsProps, HEAT, type MatchFlameState, PAPER_WHITE, PRESENCE, type PannerLike, ParticlePool, type ParticlePreset, type ParticlePresetName, type ParticleTarget, SATURATION, type SolverUniforms, type SoundAt, type SurfaceLocator, type Voice, createAudioContext, emit, emitHex, fireEmitterDefaults, fireFluidControls, fireFluidDefaults, fireZones, flameAnchors, flamePuff, fxQualityFor, fxQualityNames, fxQualityTiers, hexToLinear, luminance, particlePresets, solverUniforms, srgbToLinear, timesPaperWhite };
1820
+ export { Afterglow, type AfterglowOptions, type AudioLike, type BiquadFilterLike, CHAR, DamageField, type DamageFieldOptions, DamageSource, FIELD_SIZE, FIRE_GLOW, FIRE_HEAT_SCALE, FIRE_LIGHT_GAIN, FIRE_SOOT_SCALE, FIRE_ZONES, FIXED_DT, FLAME_HEIGHT, FX_BLOOM, FX_BLOOM_THRESHOLD, FX_INITIAL_TIER, FX_TIER_ORDER, type FieldStats, FireEmitter, type FireEmitterOptions, FireFluid, type FireFluidParams, FireSound, type FireSoundOptions, type FireZones, type FireZonesInput, type FlameAnchor, type FluidGrid, FxAudio, type FxAudioOptions, type FxFilm, FxFireFluid, type FxFireFluidProps, FxFireLight, type FxFireLightProps, FxFlames, type FxFlamesProps, FxMatchFlame, type FxMatchFlameProps, FxParticles, type FxParticlesProps, FxPost, type FxPostProps, type FxQualityName, type FxQualitySettings, type FxQualityTier, FxWisps, type FxWispsProps, HEAT, type MatchFlameState, PAPER_WHITE, PRESENCE, type PannerLike, ParticlePool, type ParticlePreset, type ParticlePresetName, type ParticleTarget, SATURATION, type SolverUniforms, type SoundAt, type SurfaceLocator, type Voice, createAudioContext, emit, emitHex, fireEmitterDefaults, fireFluidControls, fireFluidDefaults, fireZones, flameAnchors, flamePuff, fxQualityFor, fxQualityNames, fxQualityTiers, hexToLinear, luminance, particlePresets, solverUniforms, srgbToLinear, timesPaperWhite };