Hidden Realities — Milkweed & shared wind field

First plate of the hidden-realities series: natural subjects as
instrument photographs. One invisible wind field, three detectors —
dunes (time-integrated, what the sand remembers), water (instantaneous),
milkweed seeds (advected test particles).

- src/field/wind.js: divergence-free curl-noise field with closed-form
  time integral; displacement / scalar / stepped-vec samplers.
- src/scene/umbel.js: milkweed head as a particle interaction vertex;
  burst releases pedicels to seed. src/scene/drift.js: seeds advected,
  same track contract as track.js.
- composition.js: switch -> GROUP_BUILDERS registry; fieldLake/flora/
  drift layers + wind plumbing. carpet.js/perspgrid.js: warpFn/phaseFn/
  modesFn/heightFn injection hooks (default off, no regression).
- schema + composer panels, dunelake ink palette, dune-lake template,
  tools/dunelake.mjs (17-plate matrix) and its curated SVG set.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-17 15:23:55 -04:00
parent e7fb2e9a68
commit 63066ee533
47 changed files with 5022 additions and 60 deletions

View File

@@ -0,0 +1,240 @@
/* dune-lake-milkweed · breeze · big water, high dune line (CRITICALDUNE-7) */
export const composition = {
"size": 1600,
"seed": "CRITICALDUNE-7",
"wind": {
"seed": "ZEPHYR-011",
"strength": 0.55,
"scale": 1.6,
"gust": 0.4,
"time": 1.4
},
"background": {
"color": "rgb(226,216,192)",
"film": {
"opacity": 0.5,
"density": 0.5,
"seed": 8
},
"aging": {
"opacity": 0.35,
"scratches": 3,
"dust": 0.45,
"foxing": 0.5,
"seed": 5
},
"grain": {
"opacity": 0.38,
"intensity": 0.42,
"seed": 19
},
"glow": {
"strength": 0.4,
"followSun": false
},
"vignette": {
"strength": 0.32,
"mode": "radial",
"cx": 0.5,
"cy": 0.42,
"radius": 0.9,
"angle": 90,
"start": 0.5
}
},
"fieldLake": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"seed": "LIMNOS-CRITICALDUNE-7",
"color": {
"hueBack": 0.555,
"hueFront": 0.52,
"sat": 0.42,
"light": 0.52
},
"layers": 2,
"chaos": 0.1,
"blips": 0.2,
"mound": 0,
"horizonY": 0.28,
"lines": 24,
"wind": {
"warp": 0.55
},
"clip": [
0,
0.24,
1,
0.46
]
},
"fieldSea": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"seed": "AEOLIAN-CRITICALDUNE-7",
"color": {
"hueBack": 0.09,
"hueFront": 0.07,
"sat": 0.42,
"light": 0.42
},
"layers": 3,
"chaos": 0.22,
"blips": 0.35,
"mound": 0.55,
"horizonY": 0.44,
"lines": 44,
"wind": {
"warp": 0.9
}
},
"fieldGrid": {
"enabled": false,
"layerOpacity": 1,
"pos": "over",
"style": "floor",
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"color": {
"hue": 0.5,
"hue2": 0.55,
"sat": 0.32,
"lightNear": 0.34,
"lightFar": 0.62
},
"opacity": 0.44,
"pitch": 0.45,
"yaw": 0.42,
"persp": 1.1,
"dist": 2.8,
"nx": 16,
"nz": 24,
"originY": 0.34,
"ripple": {
"amp": 0,
"freqI": 0.5,
"freqK": 0.35,
"phase": 0
},
"resonance": {
"amp": 0,
"q": 14,
"axis": "ties",
"hue": 0.06,
"sat": 0.7,
"light": 0.45
},
"wind": {
"ripple": 0
}
},
"disk": {
"enabled": false,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": -0.26,
"rotation": 0,
"scale": 0.78
},
"hue": 0.06,
"sat": 0.82,
"size": 0.16,
"pressure": 0.85,
"intensity": 0.85,
"striations": 0.6,
"stain": 0.35,
"soften": 1.35
},
"bubble": {
"enabled": false,
"layerOpacity": 1,
"transform": {
"x": 0.28,
"y": -0.1,
"rotation": 10,
"scale": 0.78
},
"palette": "magentarise",
"saturation": 1.05,
"traceHue": 0,
"transparentBase": true,
"primaries": 18,
"sweepers": 5,
"cosmics": 6,
"eloss": 0.34,
"bfield": 1,
"deltaRate": 0.6,
"showBoundary": true,
"boundaryR": 0.45,
"boundaryY": 0.35,
"boundaryOpacity": 0.4,
"instrument": 0.35
},
"flora": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": -0.3,
"y": 0.5,
"rotation": 0,
"scale": 1
},
"palette": "dunelake",
"saturation": 1.05,
"count": 3,
"scale": 0.42,
"pedicels": 46,
"droop": 0.4,
"spread": 1,
"burst": 0.25,
"floretSize": 1,
"clusterW": 0.7,
"clusterH": 0.3,
"seed": "ASCLEPIAS-CRITICALDUNE-7"
},
"drift": {
"enabled": true,
"layerOpacity": 1,
"palette": "dunelake",
"count": 28,
"slip": 0.4,
"flutter": 0.55,
"settle": 0.06,
"tuft": 2
},
"fiduciaries": {
"enabled": true,
"layerOpacity": 1,
"label": "ASCLEPIAS SYRIACA",
"pencil": "#39312a",
"width": 1,
"arrow": true,
"corners": false,
"caption": "wind · integrated | instantaneous | advected",
"target": [
-0.3,
0.5
],
"from": [
0.35,
-0.3
]
}
};

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 10 MiB

View File

@@ -0,0 +1,240 @@
/* dune-lake-milkweed · breeze · big sky, dunes low (CRITICALDUNE-7) */
export const composition = {
"size": 1600,
"seed": "CRITICALDUNE-7",
"wind": {
"seed": "ZEPHYR-011",
"strength": 0.55,
"scale": 1.6,
"gust": 0.4,
"time": 1.4
},
"background": {
"color": "rgb(226,216,192)",
"film": {
"opacity": 0.5,
"density": 0.5,
"seed": 8
},
"aging": {
"opacity": 0.35,
"scratches": 3,
"dust": 0.45,
"foxing": 0.5,
"seed": 5
},
"grain": {
"opacity": 0.38,
"intensity": 0.42,
"seed": 19
},
"glow": {
"strength": 0.4,
"followSun": false
},
"vignette": {
"strength": 0.32,
"mode": "radial",
"cx": 0.5,
"cy": 0.42,
"radius": 0.9,
"angle": 90,
"start": 0.5
}
},
"fieldLake": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"seed": "LIMNOS-CRITICALDUNE-7",
"color": {
"hueBack": 0.555,
"hueFront": 0.52,
"sat": 0.42,
"light": 0.52
},
"layers": 2,
"chaos": 0.1,
"blips": 0.2,
"mound": 0,
"horizonY": 0.45999999999999996,
"lines": 24,
"wind": {
"warp": 0.55
},
"clip": [
0,
0.42,
1,
0.64
]
},
"fieldSea": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"seed": "AEOLIAN-CRITICALDUNE-7",
"color": {
"hueBack": 0.09,
"hueFront": 0.07,
"sat": 0.42,
"light": 0.42
},
"layers": 3,
"chaos": 0.22,
"blips": 0.35,
"mound": 0.55,
"horizonY": 0.62,
"lines": 52,
"wind": {
"warp": 0.9
}
},
"fieldGrid": {
"enabled": false,
"layerOpacity": 1,
"pos": "over",
"style": "floor",
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"color": {
"hue": 0.5,
"hue2": 0.55,
"sat": 0.32,
"lightNear": 0.34,
"lightFar": 0.62
},
"opacity": 0.44,
"pitch": 0.45,
"yaw": 0.42,
"persp": 1.1,
"dist": 2.8,
"nx": 16,
"nz": 24,
"originY": 0.34,
"ripple": {
"amp": 0,
"freqI": 0.5,
"freqK": 0.35,
"phase": 0
},
"resonance": {
"amp": 0,
"q": 14,
"axis": "ties",
"hue": 0.06,
"sat": 0.7,
"light": 0.45
},
"wind": {
"ripple": 0
}
},
"disk": {
"enabled": false,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": -0.26,
"rotation": 0,
"scale": 0.78
},
"hue": 0.06,
"sat": 0.82,
"size": 0.16,
"pressure": 0.85,
"intensity": 0.85,
"striations": 0.6,
"stain": 0.35,
"soften": 1.35
},
"bubble": {
"enabled": false,
"layerOpacity": 1,
"transform": {
"x": 0.28,
"y": -0.1,
"rotation": 10,
"scale": 0.78
},
"palette": "magentarise",
"saturation": 1.05,
"traceHue": 0,
"transparentBase": true,
"primaries": 18,
"sweepers": 5,
"cosmics": 6,
"eloss": 0.34,
"bfield": 1,
"deltaRate": 0.6,
"showBoundary": true,
"boundaryR": 0.45,
"boundaryY": 0.35,
"boundaryOpacity": 0.4,
"instrument": 0.35
},
"flora": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": -0.34,
"y": 0.42,
"rotation": 0,
"scale": 1
},
"palette": "dunelake",
"saturation": 1.05,
"count": 3,
"scale": 0.42,
"pedicels": 46,
"droop": 0.4,
"spread": 1,
"burst": 0.25,
"floretSize": 1,
"clusterW": 0.7,
"clusterH": 0.3,
"seed": "ASCLEPIAS-CRITICALDUNE-7"
},
"drift": {
"enabled": true,
"layerOpacity": 1,
"palette": "dunelake",
"count": 28,
"slip": 0.4,
"flutter": 0.55,
"settle": 0.06,
"tuft": 2
},
"fiduciaries": {
"enabled": true,
"layerOpacity": 1,
"label": "ASCLEPIAS SYRIACA",
"pencil": "#39312a",
"width": 1,
"arrow": true,
"corners": false,
"caption": "wind · integrated | instantaneous | advected",
"target": [
-0.34,
0.42
],
"from": [
0.35,
-0.3
]
}
};

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 MiB

View File

@@ -0,0 +1,240 @@
/* dune-lake-milkweed · gale · big water, high dune line (CRITICALDUNE-7) */
export const composition = {
"size": 1600,
"seed": "CRITICALDUNE-7",
"wind": {
"seed": "BOREAS-104",
"strength": 1.25,
"scale": 1.6,
"gust": 0.4,
"time": 1.4
},
"background": {
"color": "rgb(226,216,192)",
"film": {
"opacity": 0.5,
"density": 0.5,
"seed": 8
},
"aging": {
"opacity": 0.35,
"scratches": 3,
"dust": 0.45,
"foxing": 0.5,
"seed": 5
},
"grain": {
"opacity": 0.38,
"intensity": 0.42,
"seed": 19
},
"glow": {
"strength": 0.4,
"followSun": false
},
"vignette": {
"strength": 0.32,
"mode": "radial",
"cx": 0.5,
"cy": 0.42,
"radius": 0.9,
"angle": 90,
"start": 0.5
}
},
"fieldLake": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"seed": "LIMNOS-CRITICALDUNE-7",
"color": {
"hueBack": 0.555,
"hueFront": 0.52,
"sat": 0.42,
"light": 0.52
},
"layers": 2,
"chaos": 0.1,
"blips": 0.2,
"mound": 0,
"horizonY": 0.28,
"lines": 24,
"wind": {
"warp": 0.55
},
"clip": [
0,
0.24,
1,
0.46
]
},
"fieldSea": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"seed": "AEOLIAN-CRITICALDUNE-7",
"color": {
"hueBack": 0.09,
"hueFront": 0.07,
"sat": 0.42,
"light": 0.42
},
"layers": 3,
"chaos": 0.22,
"blips": 0.35,
"mound": 0.55,
"horizonY": 0.44,
"lines": 44,
"wind": {
"warp": 0.9
}
},
"fieldGrid": {
"enabled": false,
"layerOpacity": 1,
"pos": "over",
"style": "floor",
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"color": {
"hue": 0.5,
"hue2": 0.55,
"sat": 0.32,
"lightNear": 0.34,
"lightFar": 0.62
},
"opacity": 0.44,
"pitch": 0.45,
"yaw": 0.42,
"persp": 1.1,
"dist": 2.8,
"nx": 16,
"nz": 24,
"originY": 0.34,
"ripple": {
"amp": 0,
"freqI": 0.5,
"freqK": 0.35,
"phase": 0
},
"resonance": {
"amp": 0,
"q": 14,
"axis": "ties",
"hue": 0.06,
"sat": 0.7,
"light": 0.45
},
"wind": {
"ripple": 0
}
},
"disk": {
"enabled": false,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": -0.26,
"rotation": 0,
"scale": 0.78
},
"hue": 0.06,
"sat": 0.82,
"size": 0.16,
"pressure": 0.85,
"intensity": 0.85,
"striations": 0.6,
"stain": 0.35,
"soften": 1.35
},
"bubble": {
"enabled": false,
"layerOpacity": 1,
"transform": {
"x": 0.28,
"y": -0.1,
"rotation": 10,
"scale": 0.78
},
"palette": "magentarise",
"saturation": 1.05,
"traceHue": 0,
"transparentBase": true,
"primaries": 18,
"sweepers": 5,
"cosmics": 6,
"eloss": 0.34,
"bfield": 1,
"deltaRate": 0.6,
"showBoundary": true,
"boundaryR": 0.45,
"boundaryY": 0.35,
"boundaryOpacity": 0.4,
"instrument": 0.35
},
"flora": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": -0.3,
"y": 0.5,
"rotation": 0,
"scale": 1
},
"palette": "dunelake",
"saturation": 1.05,
"count": 3,
"scale": 0.42,
"pedicels": 46,
"droop": 0.4,
"spread": 1,
"burst": 0.25,
"floretSize": 1,
"clusterW": 0.7,
"clusterH": 0.3,
"seed": "ASCLEPIAS-CRITICALDUNE-7"
},
"drift": {
"enabled": true,
"layerOpacity": 1,
"palette": "dunelake",
"count": 28,
"slip": 0.4,
"flutter": 0.55,
"settle": 0.06,
"tuft": 2
},
"fiduciaries": {
"enabled": true,
"layerOpacity": 1,
"label": "ASCLEPIAS SYRIACA",
"pencil": "#39312a",
"width": 1,
"arrow": true,
"corners": false,
"caption": "wind · integrated | instantaneous | advected",
"target": [
-0.3,
0.5
],
"from": [
0.35,
-0.3
]
}
};

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.6 MiB

View File

@@ -0,0 +1,240 @@
/* dune-lake-milkweed · gale · big sky, dunes low (CRITICALDUNE-7) */
export const composition = {
"size": 1600,
"seed": "CRITICALDUNE-7",
"wind": {
"seed": "BOREAS-104",
"strength": 1.25,
"scale": 1.6,
"gust": 0.4,
"time": 1.4
},
"background": {
"color": "rgb(226,216,192)",
"film": {
"opacity": 0.5,
"density": 0.5,
"seed": 8
},
"aging": {
"opacity": 0.35,
"scratches": 3,
"dust": 0.45,
"foxing": 0.5,
"seed": 5
},
"grain": {
"opacity": 0.38,
"intensity": 0.42,
"seed": 19
},
"glow": {
"strength": 0.4,
"followSun": false
},
"vignette": {
"strength": 0.32,
"mode": "radial",
"cx": 0.5,
"cy": 0.42,
"radius": 0.9,
"angle": 90,
"start": 0.5
}
},
"fieldLake": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"seed": "LIMNOS-CRITICALDUNE-7",
"color": {
"hueBack": 0.555,
"hueFront": 0.52,
"sat": 0.42,
"light": 0.52
},
"layers": 2,
"chaos": 0.1,
"blips": 0.2,
"mound": 0,
"horizonY": 0.45999999999999996,
"lines": 24,
"wind": {
"warp": 0.55
},
"clip": [
0,
0.42,
1,
0.64
]
},
"fieldSea": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"seed": "AEOLIAN-CRITICALDUNE-7",
"color": {
"hueBack": 0.09,
"hueFront": 0.07,
"sat": 0.42,
"light": 0.42
},
"layers": 3,
"chaos": 0.22,
"blips": 0.35,
"mound": 0.55,
"horizonY": 0.62,
"lines": 52,
"wind": {
"warp": 0.9
}
},
"fieldGrid": {
"enabled": false,
"layerOpacity": 1,
"pos": "over",
"style": "floor",
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"color": {
"hue": 0.5,
"hue2": 0.55,
"sat": 0.32,
"lightNear": 0.34,
"lightFar": 0.62
},
"opacity": 0.44,
"pitch": 0.45,
"yaw": 0.42,
"persp": 1.1,
"dist": 2.8,
"nx": 16,
"nz": 24,
"originY": 0.34,
"ripple": {
"amp": 0,
"freqI": 0.5,
"freqK": 0.35,
"phase": 0
},
"resonance": {
"amp": 0,
"q": 14,
"axis": "ties",
"hue": 0.06,
"sat": 0.7,
"light": 0.45
},
"wind": {
"ripple": 0
}
},
"disk": {
"enabled": false,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": -0.26,
"rotation": 0,
"scale": 0.78
},
"hue": 0.06,
"sat": 0.82,
"size": 0.16,
"pressure": 0.85,
"intensity": 0.85,
"striations": 0.6,
"stain": 0.35,
"soften": 1.35
},
"bubble": {
"enabled": false,
"layerOpacity": 1,
"transform": {
"x": 0.28,
"y": -0.1,
"rotation": 10,
"scale": 0.78
},
"palette": "magentarise",
"saturation": 1.05,
"traceHue": 0,
"transparentBase": true,
"primaries": 18,
"sweepers": 5,
"cosmics": 6,
"eloss": 0.34,
"bfield": 1,
"deltaRate": 0.6,
"showBoundary": true,
"boundaryR": 0.45,
"boundaryY": 0.35,
"boundaryOpacity": 0.4,
"instrument": 0.35
},
"flora": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": -0.34,
"y": 0.42,
"rotation": 0,
"scale": 1
},
"palette": "dunelake",
"saturation": 1.05,
"count": 3,
"scale": 0.42,
"pedicels": 46,
"droop": 0.4,
"spread": 1,
"burst": 0.25,
"floretSize": 1,
"clusterW": 0.7,
"clusterH": 0.3,
"seed": "ASCLEPIAS-CRITICALDUNE-7"
},
"drift": {
"enabled": true,
"layerOpacity": 1,
"palette": "dunelake",
"count": 28,
"slip": 0.4,
"flutter": 0.55,
"settle": 0.06,
"tuft": 2
},
"fiduciaries": {
"enabled": true,
"layerOpacity": 1,
"label": "ASCLEPIAS SYRIACA",
"pencil": "#39312a",
"width": 1,
"arrow": true,
"corners": false,
"caption": "wind · integrated | instantaneous | advected",
"target": [
-0.34,
0.42
],
"from": [
0.35,
-0.3
]
}
};

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.0 MiB

View File

@@ -0,0 +1,240 @@
/* dune-lake-milkweed · breeze · big water, high dune line (LAKEMICH-0610) */
export const composition = {
"size": 1600,
"seed": "LAKEMICH-0610",
"wind": {
"seed": "ZEPHYR-011",
"strength": 0.55,
"scale": 1.6,
"gust": 0.4,
"time": 1.4
},
"background": {
"color": "rgb(226,216,192)",
"film": {
"opacity": 0.5,
"density": 0.5,
"seed": 8
},
"aging": {
"opacity": 0.35,
"scratches": 3,
"dust": 0.45,
"foxing": 0.5,
"seed": 5
},
"grain": {
"opacity": 0.38,
"intensity": 0.42,
"seed": 19
},
"glow": {
"strength": 0.4,
"followSun": false
},
"vignette": {
"strength": 0.32,
"mode": "radial",
"cx": 0.5,
"cy": 0.42,
"radius": 0.9,
"angle": 90,
"start": 0.5
}
},
"fieldLake": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"seed": "LIMNOS-LAKEMICH-0610",
"color": {
"hueBack": 0.555,
"hueFront": 0.52,
"sat": 0.42,
"light": 0.52
},
"layers": 2,
"chaos": 0.1,
"blips": 0.2,
"mound": 0,
"horizonY": 0.28,
"lines": 24,
"wind": {
"warp": 0.55
},
"clip": [
0,
0.24,
1,
0.46
]
},
"fieldSea": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"seed": "AEOLIAN-LAKEMICH-0610",
"color": {
"hueBack": 0.09,
"hueFront": 0.07,
"sat": 0.42,
"light": 0.42
},
"layers": 3,
"chaos": 0.22,
"blips": 0.35,
"mound": 0.55,
"horizonY": 0.44,
"lines": 44,
"wind": {
"warp": 0.9
}
},
"fieldGrid": {
"enabled": false,
"layerOpacity": 1,
"pos": "over",
"style": "floor",
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"color": {
"hue": 0.5,
"hue2": 0.55,
"sat": 0.32,
"lightNear": 0.34,
"lightFar": 0.62
},
"opacity": 0.44,
"pitch": 0.45,
"yaw": 0.42,
"persp": 1.1,
"dist": 2.8,
"nx": 16,
"nz": 24,
"originY": 0.34,
"ripple": {
"amp": 0,
"freqI": 0.5,
"freqK": 0.35,
"phase": 0
},
"resonance": {
"amp": 0,
"q": 14,
"axis": "ties",
"hue": 0.06,
"sat": 0.7,
"light": 0.45
},
"wind": {
"ripple": 0
}
},
"disk": {
"enabled": false,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": -0.26,
"rotation": 0,
"scale": 0.78
},
"hue": 0.06,
"sat": 0.82,
"size": 0.16,
"pressure": 0.85,
"intensity": 0.85,
"striations": 0.6,
"stain": 0.35,
"soften": 1.35
},
"bubble": {
"enabled": false,
"layerOpacity": 1,
"transform": {
"x": 0.28,
"y": -0.1,
"rotation": 10,
"scale": 0.78
},
"palette": "magentarise",
"saturation": 1.05,
"traceHue": 0,
"transparentBase": true,
"primaries": 18,
"sweepers": 5,
"cosmics": 6,
"eloss": 0.34,
"bfield": 1,
"deltaRate": 0.6,
"showBoundary": true,
"boundaryR": 0.45,
"boundaryY": 0.35,
"boundaryOpacity": 0.4,
"instrument": 0.35
},
"flora": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": -0.3,
"y": 0.5,
"rotation": 0,
"scale": 1
},
"palette": "dunelake",
"saturation": 1.05,
"count": 3,
"scale": 0.42,
"pedicels": 46,
"droop": 0.4,
"spread": 1,
"burst": 0.25,
"floretSize": 1,
"clusterW": 0.7,
"clusterH": 0.3,
"seed": "ASCLEPIAS-LAKEMICH-0610"
},
"drift": {
"enabled": true,
"layerOpacity": 1,
"palette": "dunelake",
"count": 28,
"slip": 0.4,
"flutter": 0.55,
"settle": 0.06,
"tuft": 2
},
"fiduciaries": {
"enabled": true,
"layerOpacity": 1,
"label": "ASCLEPIAS SYRIACA",
"pencil": "#39312a",
"width": 1,
"arrow": true,
"corners": false,
"caption": "wind · integrated | instantaneous | advected",
"target": [
-0.3,
0.5
],
"from": [
0.35,
-0.3
]
}
};

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 MiB

View File

@@ -0,0 +1,240 @@
/* dune-lake-milkweed · breeze · big sky, dunes low (LAKEMICH-0610) */
export const composition = {
"size": 1600,
"seed": "LAKEMICH-0610",
"wind": {
"seed": "ZEPHYR-011",
"strength": 0.55,
"scale": 1.6,
"gust": 0.4,
"time": 1.4
},
"background": {
"color": "rgb(226,216,192)",
"film": {
"opacity": 0.5,
"density": 0.5,
"seed": 8
},
"aging": {
"opacity": 0.35,
"scratches": 3,
"dust": 0.45,
"foxing": 0.5,
"seed": 5
},
"grain": {
"opacity": 0.38,
"intensity": 0.42,
"seed": 19
},
"glow": {
"strength": 0.4,
"followSun": false
},
"vignette": {
"strength": 0.32,
"mode": "radial",
"cx": 0.5,
"cy": 0.42,
"radius": 0.9,
"angle": 90,
"start": 0.5
}
},
"fieldLake": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"seed": "LIMNOS-LAKEMICH-0610",
"color": {
"hueBack": 0.555,
"hueFront": 0.52,
"sat": 0.42,
"light": 0.52
},
"layers": 2,
"chaos": 0.1,
"blips": 0.2,
"mound": 0,
"horizonY": 0.45999999999999996,
"lines": 24,
"wind": {
"warp": 0.55
},
"clip": [
0,
0.42,
1,
0.64
]
},
"fieldSea": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"seed": "AEOLIAN-LAKEMICH-0610",
"color": {
"hueBack": 0.09,
"hueFront": 0.07,
"sat": 0.42,
"light": 0.42
},
"layers": 3,
"chaos": 0.22,
"blips": 0.35,
"mound": 0.55,
"horizonY": 0.62,
"lines": 52,
"wind": {
"warp": 0.9
}
},
"fieldGrid": {
"enabled": false,
"layerOpacity": 1,
"pos": "over",
"style": "floor",
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"color": {
"hue": 0.5,
"hue2": 0.55,
"sat": 0.32,
"lightNear": 0.34,
"lightFar": 0.62
},
"opacity": 0.44,
"pitch": 0.45,
"yaw": 0.42,
"persp": 1.1,
"dist": 2.8,
"nx": 16,
"nz": 24,
"originY": 0.34,
"ripple": {
"amp": 0,
"freqI": 0.5,
"freqK": 0.35,
"phase": 0
},
"resonance": {
"amp": 0,
"q": 14,
"axis": "ties",
"hue": 0.06,
"sat": 0.7,
"light": 0.45
},
"wind": {
"ripple": 0
}
},
"disk": {
"enabled": false,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": -0.26,
"rotation": 0,
"scale": 0.78
},
"hue": 0.06,
"sat": 0.82,
"size": 0.16,
"pressure": 0.85,
"intensity": 0.85,
"striations": 0.6,
"stain": 0.35,
"soften": 1.35
},
"bubble": {
"enabled": false,
"layerOpacity": 1,
"transform": {
"x": 0.28,
"y": -0.1,
"rotation": 10,
"scale": 0.78
},
"palette": "magentarise",
"saturation": 1.05,
"traceHue": 0,
"transparentBase": true,
"primaries": 18,
"sweepers": 5,
"cosmics": 6,
"eloss": 0.34,
"bfield": 1,
"deltaRate": 0.6,
"showBoundary": true,
"boundaryR": 0.45,
"boundaryY": 0.35,
"boundaryOpacity": 0.4,
"instrument": 0.35
},
"flora": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": -0.34,
"y": 0.42,
"rotation": 0,
"scale": 1
},
"palette": "dunelake",
"saturation": 1.05,
"count": 3,
"scale": 0.42,
"pedicels": 46,
"droop": 0.4,
"spread": 1,
"burst": 0.25,
"floretSize": 1,
"clusterW": 0.7,
"clusterH": 0.3,
"seed": "ASCLEPIAS-LAKEMICH-0610"
},
"drift": {
"enabled": true,
"layerOpacity": 1,
"palette": "dunelake",
"count": 28,
"slip": 0.4,
"flutter": 0.55,
"settle": 0.06,
"tuft": 2
},
"fiduciaries": {
"enabled": true,
"layerOpacity": 1,
"label": "ASCLEPIAS SYRIACA",
"pencil": "#39312a",
"width": 1,
"arrow": true,
"corners": false,
"caption": "wind · integrated | instantaneous | advected",
"target": [
-0.34,
0.42
],
"from": [
0.35,
-0.3
]
}
};

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 MiB

View File

@@ -0,0 +1,240 @@
/* dune-lake-milkweed · gale · big water, high dune line (LAKEMICH-0610) */
export const composition = {
"size": 1600,
"seed": "LAKEMICH-0610",
"wind": {
"seed": "BOREAS-104",
"strength": 1.25,
"scale": 1.6,
"gust": 0.4,
"time": 1.4
},
"background": {
"color": "rgb(226,216,192)",
"film": {
"opacity": 0.5,
"density": 0.5,
"seed": 8
},
"aging": {
"opacity": 0.35,
"scratches": 3,
"dust": 0.45,
"foxing": 0.5,
"seed": 5
},
"grain": {
"opacity": 0.38,
"intensity": 0.42,
"seed": 19
},
"glow": {
"strength": 0.4,
"followSun": false
},
"vignette": {
"strength": 0.32,
"mode": "radial",
"cx": 0.5,
"cy": 0.42,
"radius": 0.9,
"angle": 90,
"start": 0.5
}
},
"fieldLake": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"seed": "LIMNOS-LAKEMICH-0610",
"color": {
"hueBack": 0.555,
"hueFront": 0.52,
"sat": 0.42,
"light": 0.52
},
"layers": 2,
"chaos": 0.1,
"blips": 0.2,
"mound": 0,
"horizonY": 0.28,
"lines": 24,
"wind": {
"warp": 0.55
},
"clip": [
0,
0.24,
1,
0.46
]
},
"fieldSea": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"seed": "AEOLIAN-LAKEMICH-0610",
"color": {
"hueBack": 0.09,
"hueFront": 0.07,
"sat": 0.42,
"light": 0.42
},
"layers": 3,
"chaos": 0.22,
"blips": 0.35,
"mound": 0.55,
"horizonY": 0.44,
"lines": 44,
"wind": {
"warp": 0.9
}
},
"fieldGrid": {
"enabled": false,
"layerOpacity": 1,
"pos": "over",
"style": "floor",
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"color": {
"hue": 0.5,
"hue2": 0.55,
"sat": 0.32,
"lightNear": 0.34,
"lightFar": 0.62
},
"opacity": 0.44,
"pitch": 0.45,
"yaw": 0.42,
"persp": 1.1,
"dist": 2.8,
"nx": 16,
"nz": 24,
"originY": 0.34,
"ripple": {
"amp": 0,
"freqI": 0.5,
"freqK": 0.35,
"phase": 0
},
"resonance": {
"amp": 0,
"q": 14,
"axis": "ties",
"hue": 0.06,
"sat": 0.7,
"light": 0.45
},
"wind": {
"ripple": 0
}
},
"disk": {
"enabled": false,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": -0.26,
"rotation": 0,
"scale": 0.78
},
"hue": 0.06,
"sat": 0.82,
"size": 0.16,
"pressure": 0.85,
"intensity": 0.85,
"striations": 0.6,
"stain": 0.35,
"soften": 1.35
},
"bubble": {
"enabled": false,
"layerOpacity": 1,
"transform": {
"x": 0.28,
"y": -0.1,
"rotation": 10,
"scale": 0.78
},
"palette": "magentarise",
"saturation": 1.05,
"traceHue": 0,
"transparentBase": true,
"primaries": 18,
"sweepers": 5,
"cosmics": 6,
"eloss": 0.34,
"bfield": 1,
"deltaRate": 0.6,
"showBoundary": true,
"boundaryR": 0.45,
"boundaryY": 0.35,
"boundaryOpacity": 0.4,
"instrument": 0.35
},
"flora": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": -0.3,
"y": 0.5,
"rotation": 0,
"scale": 1
},
"palette": "dunelake",
"saturation": 1.05,
"count": 3,
"scale": 0.42,
"pedicels": 46,
"droop": 0.4,
"spread": 1,
"burst": 0.25,
"floretSize": 1,
"clusterW": 0.7,
"clusterH": 0.3,
"seed": "ASCLEPIAS-LAKEMICH-0610"
},
"drift": {
"enabled": true,
"layerOpacity": 1,
"palette": "dunelake",
"count": 28,
"slip": 0.4,
"flutter": 0.55,
"settle": 0.06,
"tuft": 2
},
"fiduciaries": {
"enabled": true,
"layerOpacity": 1,
"label": "ASCLEPIAS SYRIACA",
"pencil": "#39312a",
"width": 1,
"arrow": true,
"corners": false,
"caption": "wind · integrated | instantaneous | advected",
"target": [
-0.3,
0.5
],
"from": [
0.35,
-0.3
]
}
};

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.7 MiB

View File

@@ -0,0 +1,240 @@
/* dune-lake-milkweed · gale · big sky, dunes low (LAKEMICH-0610) */
export const composition = {
"size": 1600,
"seed": "LAKEMICH-0610",
"wind": {
"seed": "BOREAS-104",
"strength": 1.25,
"scale": 1.6,
"gust": 0.4,
"time": 1.4
},
"background": {
"color": "rgb(226,216,192)",
"film": {
"opacity": 0.5,
"density": 0.5,
"seed": 8
},
"aging": {
"opacity": 0.35,
"scratches": 3,
"dust": 0.45,
"foxing": 0.5,
"seed": 5
},
"grain": {
"opacity": 0.38,
"intensity": 0.42,
"seed": 19
},
"glow": {
"strength": 0.4,
"followSun": false
},
"vignette": {
"strength": 0.32,
"mode": "radial",
"cx": 0.5,
"cy": 0.42,
"radius": 0.9,
"angle": 90,
"start": 0.5
}
},
"fieldLake": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"seed": "LIMNOS-LAKEMICH-0610",
"color": {
"hueBack": 0.555,
"hueFront": 0.52,
"sat": 0.42,
"light": 0.52
},
"layers": 2,
"chaos": 0.1,
"blips": 0.2,
"mound": 0,
"horizonY": 0.45999999999999996,
"lines": 24,
"wind": {
"warp": 0.55
},
"clip": [
0,
0.42,
1,
0.64
]
},
"fieldSea": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"seed": "AEOLIAN-LAKEMICH-0610",
"color": {
"hueBack": 0.09,
"hueFront": 0.07,
"sat": 0.42,
"light": 0.42
},
"layers": 3,
"chaos": 0.22,
"blips": 0.35,
"mound": 0.55,
"horizonY": 0.62,
"lines": 52,
"wind": {
"warp": 0.9
}
},
"fieldGrid": {
"enabled": false,
"layerOpacity": 1,
"pos": "over",
"style": "floor",
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"color": {
"hue": 0.5,
"hue2": 0.55,
"sat": 0.32,
"lightNear": 0.34,
"lightFar": 0.62
},
"opacity": 0.44,
"pitch": 0.45,
"yaw": 0.42,
"persp": 1.1,
"dist": 2.8,
"nx": 16,
"nz": 24,
"originY": 0.34,
"ripple": {
"amp": 0,
"freqI": 0.5,
"freqK": 0.35,
"phase": 0
},
"resonance": {
"amp": 0,
"q": 14,
"axis": "ties",
"hue": 0.06,
"sat": 0.7,
"light": 0.45
},
"wind": {
"ripple": 0
}
},
"disk": {
"enabled": false,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": -0.26,
"rotation": 0,
"scale": 0.78
},
"hue": 0.06,
"sat": 0.82,
"size": 0.16,
"pressure": 0.85,
"intensity": 0.85,
"striations": 0.6,
"stain": 0.35,
"soften": 1.35
},
"bubble": {
"enabled": false,
"layerOpacity": 1,
"transform": {
"x": 0.28,
"y": -0.1,
"rotation": 10,
"scale": 0.78
},
"palette": "magentarise",
"saturation": 1.05,
"traceHue": 0,
"transparentBase": true,
"primaries": 18,
"sweepers": 5,
"cosmics": 6,
"eloss": 0.34,
"bfield": 1,
"deltaRate": 0.6,
"showBoundary": true,
"boundaryR": 0.45,
"boundaryY": 0.35,
"boundaryOpacity": 0.4,
"instrument": 0.35
},
"flora": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": -0.34,
"y": 0.42,
"rotation": 0,
"scale": 1
},
"palette": "dunelake",
"saturation": 1.05,
"count": 3,
"scale": 0.42,
"pedicels": 46,
"droop": 0.4,
"spread": 1,
"burst": 0.25,
"floretSize": 1,
"clusterW": 0.7,
"clusterH": 0.3,
"seed": "ASCLEPIAS-LAKEMICH-0610"
},
"drift": {
"enabled": true,
"layerOpacity": 1,
"palette": "dunelake",
"count": 28,
"slip": 0.4,
"flutter": 0.55,
"settle": 0.06,
"tuft": 2
},
"fiduciaries": {
"enabled": true,
"layerOpacity": 1,
"label": "ASCLEPIAS SYRIACA",
"pencil": "#39312a",
"width": 1,
"arrow": true,
"corners": false,
"caption": "wind · integrated | instantaneous | advected",
"target": [
-0.34,
0.42
],
"from": [
0.35,
-0.3
]
}
};

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.4 MiB

View File

@@ -0,0 +1,5 @@
<!DOCTYPE html><html><head><meta charset="utf-8"><title>Dunes on a Great Lake — one wind, three detectors</title>
<style>html,body{margin:0;background:#15140f;color:#cabfa6;font:12px ui-monospace,monospace}h1{font-weight:400;letter-spacing:.2em;text-transform:uppercase;color:#9fb7af;padding:18px 14px 0}p.sub{color:#8a8068;padding:0 14px;margin:6px 0 0}.grid{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;padding:14px}figure{margin:0;position:relative;background:#fff;overflow:hidden}img{width:100%;display:block}figcaption{position:absolute;left:0;bottom:0;right:0;padding:6px 9px;font:12px ui-monospace,monospace;color:#fff;background:linear-gradient(transparent,#000d)}</style></head><body>
<h1>Dunes on a Great Lake with Milkweed (17)</h1>
<p class="sub">one invisible wind field · three detectors — sand (integrated) · water (instantaneous) · seeds (advected)</p>
<div class="grid"><figure><img src="lakemich-breeze_low-horizon.svg"><figcaption>breeze · big sky, dunes low (LAKEMICH-0610)</figcaption></figure><figure><img src="lakemich-breeze_high-water.svg"><figcaption>breeze · big water, high dune line (LAKEMICH-0610)</figcaption></figure><figure><img src="lakemich-gale_low-horizon.svg"><figcaption>gale · big sky, dunes low (LAKEMICH-0610)</figcaption></figure><figure><img src="lakemich-gale_high-water.svg"><figcaption>gale · big water, high dune line (LAKEMICH-0610)</figcaption></figure><figure><img src="sleeping-breeze_low-horizon.svg"><figcaption>breeze · big sky, dunes low (SLEEPING-BEAR-22)</figcaption></figure><figure><img src="sleeping-breeze_high-water.svg"><figcaption>breeze · big water, high dune line (SLEEPING-BEAR-22)</figcaption></figure><figure><img src="sleeping-gale_low-horizon.svg"><figcaption>gale · big sky, dunes low (SLEEPING-BEAR-22)</figcaption></figure><figure><img src="sleeping-gale_high-water.svg"><figcaption>gale · big water, high dune line (SLEEPING-BEAR-22)</figcaption></figure><figure><img src="criticaldune-breeze_low-horizon.svg"><figcaption>breeze · big sky, dunes low (CRITICALDUNE-7)</figcaption></figure><figure><img src="criticaldune-breeze_high-water.svg"><figcaption>breeze · big water, high dune line (CRITICALDUNE-7)</figcaption></figure><figure><img src="criticaldune-gale_low-horizon.svg"><figcaption>gale · big sky, dunes low (CRITICALDUNE-7)</figcaption></figure><figure><img src="criticaldune-gale_high-water.svg"><figcaption>gale · big water, high dune line (CRITICALDUNE-7)</figcaption></figure><figure><img src="states_closed-left.svg"><figcaption>umbels closed — the vertex before the decay</figcaption></figure><figure><img src="states_bursting-right.svg"><figcaption>right-third bursting — half the florets gone to silk</figcaption></figure><figure><img src="states_full-release.svg"><figcaption>off-corner full burst — seeds streaming across the water</figcaption></figure><figure><img src="palette_dusk.svg"><figcaption>dusk — sand holds the late light, slate water</figcaption></figure><figure><img src="palette_nocturne.svg"><figcaption>nocturne — dark plate, pale silks (night-plate move)</figcaption></figure></div></body></html>

View File

@@ -0,0 +1,240 @@
/* dune-lake-milkweed · dusk — sand holds the late light, slate water */
export const composition = {
"size": 1600,
"seed": "CRITICALDUNE-7",
"wind": {
"seed": "ZEPHYR-011",
"strength": 0.8,
"scale": 1.6,
"gust": 0.4,
"time": 1.4
},
"background": {
"color": "rgb(210,190,170)",
"film": {
"opacity": 0.5,
"density": 0.5,
"seed": 8
},
"aging": {
"opacity": 0.35,
"scratches": 3,
"dust": 0.45,
"foxing": 0.5,
"seed": 5
},
"grain": {
"opacity": 0.38,
"intensity": 0.42,
"seed": 19
},
"glow": {
"strength": 0.5,
"followSun": false
},
"vignette": {
"strength": 0.32,
"mode": "radial",
"cx": 0.5,
"cy": 0.42,
"radius": 0.9,
"angle": 90,
"start": 0.5
}
},
"fieldLake": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"seed": "LIMNOS-CRITICALDUNE-7",
"color": {
"hueBack": 0.62,
"hueFront": 0.58,
"sat": 0.5,
"light": 0.42
},
"layers": 2,
"chaos": 0.1,
"blips": 0.2,
"mound": 0,
"horizonY": 0.39,
"lines": 24,
"wind": {
"warp": 0.55
},
"clip": [
0,
0.35000000000000003,
1,
0.5700000000000001
]
},
"fieldSea": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"seed": "AEOLIAN-CRITICALDUNE-7",
"color": {
"hueBack": 0.05,
"hueFront": 0.95,
"sat": 0.5,
"light": 0.4
},
"layers": 3,
"chaos": 0.22,
"blips": 0.35,
"mound": 0.55,
"horizonY": 0.55,
"lines": 52,
"wind": {
"warp": 0.9
}
},
"fieldGrid": {
"enabled": false,
"layerOpacity": 1,
"pos": "over",
"style": "floor",
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"color": {
"hue": 0.5,
"hue2": 0.55,
"sat": 0.32,
"lightNear": 0.34,
"lightFar": 0.62
},
"opacity": 0.44,
"pitch": 0.45,
"yaw": 0.42,
"persp": 1.1,
"dist": 2.8,
"nx": 16,
"nz": 24,
"originY": 0.34,
"ripple": {
"amp": 0,
"freqI": 0.5,
"freqK": 0.35,
"phase": 0
},
"resonance": {
"amp": 0,
"q": 14,
"axis": "ties",
"hue": 0.06,
"sat": 0.7,
"light": 0.45
},
"wind": {
"ripple": 0
}
},
"disk": {
"enabled": false,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": -0.26,
"rotation": 0,
"scale": 0.78
},
"hue": 0.06,
"sat": 0.82,
"size": 0.16,
"pressure": 0.85,
"intensity": 0.85,
"striations": 0.6,
"stain": 0.35,
"soften": 1.35
},
"bubble": {
"enabled": false,
"layerOpacity": 1,
"transform": {
"x": 0.28,
"y": -0.1,
"rotation": 10,
"scale": 0.78
},
"palette": "magentarise",
"saturation": 1.05,
"traceHue": 0,
"transparentBase": true,
"primaries": 18,
"sweepers": 5,
"cosmics": 6,
"eloss": 0.34,
"bfield": 1,
"deltaRate": 0.6,
"showBoundary": true,
"boundaryR": 0.45,
"boundaryY": 0.35,
"boundaryOpacity": 0.4,
"instrument": 0.35
},
"flora": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": -0.34,
"y": 0.42,
"rotation": 0,
"scale": 1
},
"palette": "dunelake",
"saturation": 1.05,
"count": 3,
"scale": 0.42,
"pedicels": 46,
"droop": 0.4,
"spread": 1,
"burst": 0.25,
"floretSize": 1,
"clusterW": 0.7,
"clusterH": 0.3,
"seed": "ASCLEPIAS-CRITICALDUNE-7"
},
"drift": {
"enabled": true,
"layerOpacity": 1,
"palette": "dunelake",
"count": 28,
"slip": 0.4,
"flutter": 0.55,
"settle": 0.06,
"tuft": 2
},
"fiduciaries": {
"enabled": true,
"layerOpacity": 1,
"label": "ASCLEPIAS SYRIACA",
"pencil": "#39312a",
"width": 1,
"arrow": true,
"corners": false,
"caption": "wind · integrated | instantaneous | advected",
"target": [
-0.34,
0.42
],
"from": [
0.35,
-0.3
]
}
};

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.3 MiB

View File

@@ -0,0 +1,240 @@
/* dune-lake-milkweed · nocturne — dark plate, pale silks (night-plate move) */
export const composition = {
"size": 1600,
"seed": "CRITICALDUNE-7",
"wind": {
"seed": "BOREAS-104",
"strength": 1.1,
"scale": 1.6,
"gust": 0.4,
"time": 1.4
},
"background": {
"color": "rgb(22,23,30)",
"film": {
"opacity": 0.28,
"density": 0.5,
"seed": 8
},
"aging": {
"opacity": 0.35,
"scratches": 3,
"dust": 0.45,
"foxing": 0.5,
"seed": 5
},
"grain": {
"opacity": 0.3,
"intensity": 0.42,
"seed": 19
},
"glow": {
"strength": 0.3,
"followSun": false
},
"vignette": {
"strength": 0.2,
"mode": "radial",
"cx": 0.5,
"cy": 0.42,
"radius": 0.9,
"angle": 90,
"start": 0.5
}
},
"fieldLake": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"seed": "LIMNOS-CRITICALDUNE-7",
"color": {
"hueBack": 0.58,
"hueFront": 0.56,
"sat": 0.4,
"light": 0.66
},
"layers": 2,
"chaos": 0.1,
"blips": 0.2,
"mound": 0,
"horizonY": 0.39,
"lines": 24,
"wind": {
"warp": 0.55
},
"clip": [
0,
0.35000000000000003,
1,
0.5700000000000001
]
},
"fieldSea": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"seed": "AEOLIAN-CRITICALDUNE-7",
"color": {
"hueBack": 0.6,
"hueFront": 0.08,
"sat": 0.3,
"light": 0.62
},
"layers": 3,
"chaos": 0.22,
"blips": 0.35,
"mound": 0.55,
"horizonY": 0.55,
"lines": 52,
"wind": {
"warp": 0.9
}
},
"fieldGrid": {
"enabled": false,
"layerOpacity": 1,
"pos": "over",
"style": "floor",
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"color": {
"hue": 0.5,
"hue2": 0.55,
"sat": 0.32,
"lightNear": 0.34,
"lightFar": 0.62
},
"opacity": 0.44,
"pitch": 0.45,
"yaw": 0.42,
"persp": 1.1,
"dist": 2.8,
"nx": 16,
"nz": 24,
"originY": 0.34,
"ripple": {
"amp": 0,
"freqI": 0.5,
"freqK": 0.35,
"phase": 0
},
"resonance": {
"amp": 0,
"q": 14,
"axis": "ties",
"hue": 0.06,
"sat": 0.7,
"light": 0.45
},
"wind": {
"ripple": 0
}
},
"disk": {
"enabled": false,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": -0.26,
"rotation": 0,
"scale": 0.78
},
"hue": 0.06,
"sat": 0.82,
"size": 0.16,
"pressure": 0.85,
"intensity": 0.85,
"striations": 0.6,
"stain": 0.35,
"soften": 1.35
},
"bubble": {
"enabled": false,
"layerOpacity": 1,
"transform": {
"x": 0.28,
"y": -0.1,
"rotation": 10,
"scale": 0.78
},
"palette": "magentarise",
"saturation": 1.05,
"traceHue": 0,
"transparentBase": true,
"primaries": 18,
"sweepers": 5,
"cosmics": 6,
"eloss": 0.34,
"bfield": 1,
"deltaRate": 0.6,
"showBoundary": true,
"boundaryR": 0.45,
"boundaryY": 0.35,
"boundaryOpacity": 0.4,
"instrument": 0.35
},
"flora": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": -0.34,
"y": 0.42,
"rotation": 0,
"scale": 1
},
"palette": "dunelake",
"saturation": 1.05,
"count": 3,
"scale": 0.42,
"pedicels": 46,
"droop": 0.4,
"spread": 1,
"burst": 0.6,
"floretSize": 1,
"clusterW": 0.7,
"clusterH": 0.3,
"seed": "ASCLEPIAS-CRITICALDUNE-7"
},
"drift": {
"enabled": true,
"layerOpacity": 1,
"palette": "dunelake",
"count": 48,
"slip": 0.4,
"flutter": 0.55,
"settle": 0.06,
"tuft": 2
},
"fiduciaries": {
"enabled": true,
"layerOpacity": 1,
"label": "ASCLEPIAS SYRIACA",
"pencil": "#cfc4a8",
"width": 1,
"arrow": true,
"corners": false,
"caption": "wind · integrated | instantaneous | advected",
"target": [
-0.34,
0.42
],
"from": [
0.35,
-0.3
]
}
};

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 MiB

View File

@@ -0,0 +1,240 @@
/* dune-lake-milkweed · breeze · big water, high dune line (SLEEPING-BEAR-22) */
export const composition = {
"size": 1600,
"seed": "SLEEPING-BEAR-22",
"wind": {
"seed": "ZEPHYR-011",
"strength": 0.55,
"scale": 1.6,
"gust": 0.4,
"time": 1.4
},
"background": {
"color": "rgb(226,216,192)",
"film": {
"opacity": 0.5,
"density": 0.5,
"seed": 8
},
"aging": {
"opacity": 0.35,
"scratches": 3,
"dust": 0.45,
"foxing": 0.5,
"seed": 5
},
"grain": {
"opacity": 0.38,
"intensity": 0.42,
"seed": 19
},
"glow": {
"strength": 0.4,
"followSun": false
},
"vignette": {
"strength": 0.32,
"mode": "radial",
"cx": 0.5,
"cy": 0.42,
"radius": 0.9,
"angle": 90,
"start": 0.5
}
},
"fieldLake": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"seed": "LIMNOS-SLEEPING-BEAR-22",
"color": {
"hueBack": 0.555,
"hueFront": 0.52,
"sat": 0.42,
"light": 0.52
},
"layers": 2,
"chaos": 0.1,
"blips": 0.2,
"mound": 0,
"horizonY": 0.28,
"lines": 24,
"wind": {
"warp": 0.55
},
"clip": [
0,
0.24,
1,
0.46
]
},
"fieldSea": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"seed": "AEOLIAN-SLEEPING-BEAR-22",
"color": {
"hueBack": 0.09,
"hueFront": 0.07,
"sat": 0.42,
"light": 0.42
},
"layers": 3,
"chaos": 0.22,
"blips": 0.35,
"mound": 0.55,
"horizonY": 0.44,
"lines": 44,
"wind": {
"warp": 0.9
}
},
"fieldGrid": {
"enabled": false,
"layerOpacity": 1,
"pos": "over",
"style": "floor",
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"color": {
"hue": 0.5,
"hue2": 0.55,
"sat": 0.32,
"lightNear": 0.34,
"lightFar": 0.62
},
"opacity": 0.44,
"pitch": 0.45,
"yaw": 0.42,
"persp": 1.1,
"dist": 2.8,
"nx": 16,
"nz": 24,
"originY": 0.34,
"ripple": {
"amp": 0,
"freqI": 0.5,
"freqK": 0.35,
"phase": 0
},
"resonance": {
"amp": 0,
"q": 14,
"axis": "ties",
"hue": 0.06,
"sat": 0.7,
"light": 0.45
},
"wind": {
"ripple": 0
}
},
"disk": {
"enabled": false,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": -0.26,
"rotation": 0,
"scale": 0.78
},
"hue": 0.06,
"sat": 0.82,
"size": 0.16,
"pressure": 0.85,
"intensity": 0.85,
"striations": 0.6,
"stain": 0.35,
"soften": 1.35
},
"bubble": {
"enabled": false,
"layerOpacity": 1,
"transform": {
"x": 0.28,
"y": -0.1,
"rotation": 10,
"scale": 0.78
},
"palette": "magentarise",
"saturation": 1.05,
"traceHue": 0,
"transparentBase": true,
"primaries": 18,
"sweepers": 5,
"cosmics": 6,
"eloss": 0.34,
"bfield": 1,
"deltaRate": 0.6,
"showBoundary": true,
"boundaryR": 0.45,
"boundaryY": 0.35,
"boundaryOpacity": 0.4,
"instrument": 0.35
},
"flora": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": -0.3,
"y": 0.5,
"rotation": 0,
"scale": 1
},
"palette": "dunelake",
"saturation": 1.05,
"count": 3,
"scale": 0.42,
"pedicels": 46,
"droop": 0.4,
"spread": 1,
"burst": 0.25,
"floretSize": 1,
"clusterW": 0.7,
"clusterH": 0.3,
"seed": "ASCLEPIAS-SLEEPING-BEAR-22"
},
"drift": {
"enabled": true,
"layerOpacity": 1,
"palette": "dunelake",
"count": 28,
"slip": 0.4,
"flutter": 0.55,
"settle": 0.06,
"tuft": 2
},
"fiduciaries": {
"enabled": true,
"layerOpacity": 1,
"label": "ASCLEPIAS SYRIACA",
"pencil": "#39312a",
"width": 1,
"arrow": true,
"corners": false,
"caption": "wind · integrated | instantaneous | advected",
"target": [
-0.3,
0.5
],
"from": [
0.35,
-0.3
]
}
};

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.4 MiB

View File

@@ -0,0 +1,240 @@
/* dune-lake-milkweed · breeze · big sky, dunes low (SLEEPING-BEAR-22) */
export const composition = {
"size": 1600,
"seed": "SLEEPING-BEAR-22",
"wind": {
"seed": "ZEPHYR-011",
"strength": 0.55,
"scale": 1.6,
"gust": 0.4,
"time": 1.4
},
"background": {
"color": "rgb(226,216,192)",
"film": {
"opacity": 0.5,
"density": 0.5,
"seed": 8
},
"aging": {
"opacity": 0.35,
"scratches": 3,
"dust": 0.45,
"foxing": 0.5,
"seed": 5
},
"grain": {
"opacity": 0.38,
"intensity": 0.42,
"seed": 19
},
"glow": {
"strength": 0.4,
"followSun": false
},
"vignette": {
"strength": 0.32,
"mode": "radial",
"cx": 0.5,
"cy": 0.42,
"radius": 0.9,
"angle": 90,
"start": 0.5
}
},
"fieldLake": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"seed": "LIMNOS-SLEEPING-BEAR-22",
"color": {
"hueBack": 0.555,
"hueFront": 0.52,
"sat": 0.42,
"light": 0.52
},
"layers": 2,
"chaos": 0.1,
"blips": 0.2,
"mound": 0,
"horizonY": 0.45999999999999996,
"lines": 24,
"wind": {
"warp": 0.55
},
"clip": [
0,
0.42,
1,
0.64
]
},
"fieldSea": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"seed": "AEOLIAN-SLEEPING-BEAR-22",
"color": {
"hueBack": 0.09,
"hueFront": 0.07,
"sat": 0.42,
"light": 0.42
},
"layers": 3,
"chaos": 0.22,
"blips": 0.35,
"mound": 0.55,
"horizonY": 0.62,
"lines": 52,
"wind": {
"warp": 0.9
}
},
"fieldGrid": {
"enabled": false,
"layerOpacity": 1,
"pos": "over",
"style": "floor",
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"color": {
"hue": 0.5,
"hue2": 0.55,
"sat": 0.32,
"lightNear": 0.34,
"lightFar": 0.62
},
"opacity": 0.44,
"pitch": 0.45,
"yaw": 0.42,
"persp": 1.1,
"dist": 2.8,
"nx": 16,
"nz": 24,
"originY": 0.34,
"ripple": {
"amp": 0,
"freqI": 0.5,
"freqK": 0.35,
"phase": 0
},
"resonance": {
"amp": 0,
"q": 14,
"axis": "ties",
"hue": 0.06,
"sat": 0.7,
"light": 0.45
},
"wind": {
"ripple": 0
}
},
"disk": {
"enabled": false,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": -0.26,
"rotation": 0,
"scale": 0.78
},
"hue": 0.06,
"sat": 0.82,
"size": 0.16,
"pressure": 0.85,
"intensity": 0.85,
"striations": 0.6,
"stain": 0.35,
"soften": 1.35
},
"bubble": {
"enabled": false,
"layerOpacity": 1,
"transform": {
"x": 0.28,
"y": -0.1,
"rotation": 10,
"scale": 0.78
},
"palette": "magentarise",
"saturation": 1.05,
"traceHue": 0,
"transparentBase": true,
"primaries": 18,
"sweepers": 5,
"cosmics": 6,
"eloss": 0.34,
"bfield": 1,
"deltaRate": 0.6,
"showBoundary": true,
"boundaryR": 0.45,
"boundaryY": 0.35,
"boundaryOpacity": 0.4,
"instrument": 0.35
},
"flora": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": -0.34,
"y": 0.42,
"rotation": 0,
"scale": 1
},
"palette": "dunelake",
"saturation": 1.05,
"count": 3,
"scale": 0.42,
"pedicels": 46,
"droop": 0.4,
"spread": 1,
"burst": 0.25,
"floretSize": 1,
"clusterW": 0.7,
"clusterH": 0.3,
"seed": "ASCLEPIAS-SLEEPING-BEAR-22"
},
"drift": {
"enabled": true,
"layerOpacity": 1,
"palette": "dunelake",
"count": 28,
"slip": 0.4,
"flutter": 0.55,
"settle": 0.06,
"tuft": 2
},
"fiduciaries": {
"enabled": true,
"layerOpacity": 1,
"label": "ASCLEPIAS SYRIACA",
"pencil": "#39312a",
"width": 1,
"arrow": true,
"corners": false,
"caption": "wind · integrated | instantaneous | advected",
"target": [
-0.34,
0.42
],
"from": [
0.35,
-0.3
]
}
};

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.7 MiB

View File

@@ -0,0 +1,240 @@
/* dune-lake-milkweed · gale · big water, high dune line (SLEEPING-BEAR-22) */
export const composition = {
"size": 1600,
"seed": "SLEEPING-BEAR-22",
"wind": {
"seed": "BOREAS-104",
"strength": 1.25,
"scale": 1.6,
"gust": 0.4,
"time": 1.4
},
"background": {
"color": "rgb(226,216,192)",
"film": {
"opacity": 0.5,
"density": 0.5,
"seed": 8
},
"aging": {
"opacity": 0.35,
"scratches": 3,
"dust": 0.45,
"foxing": 0.5,
"seed": 5
},
"grain": {
"opacity": 0.38,
"intensity": 0.42,
"seed": 19
},
"glow": {
"strength": 0.4,
"followSun": false
},
"vignette": {
"strength": 0.32,
"mode": "radial",
"cx": 0.5,
"cy": 0.42,
"radius": 0.9,
"angle": 90,
"start": 0.5
}
},
"fieldLake": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"seed": "LIMNOS-SLEEPING-BEAR-22",
"color": {
"hueBack": 0.555,
"hueFront": 0.52,
"sat": 0.42,
"light": 0.52
},
"layers": 2,
"chaos": 0.1,
"blips": 0.2,
"mound": 0,
"horizonY": 0.28,
"lines": 24,
"wind": {
"warp": 0.55
},
"clip": [
0,
0.24,
1,
0.46
]
},
"fieldSea": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"seed": "AEOLIAN-SLEEPING-BEAR-22",
"color": {
"hueBack": 0.09,
"hueFront": 0.07,
"sat": 0.42,
"light": 0.42
},
"layers": 3,
"chaos": 0.22,
"blips": 0.35,
"mound": 0.55,
"horizonY": 0.44,
"lines": 44,
"wind": {
"warp": 0.9
}
},
"fieldGrid": {
"enabled": false,
"layerOpacity": 1,
"pos": "over",
"style": "floor",
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"color": {
"hue": 0.5,
"hue2": 0.55,
"sat": 0.32,
"lightNear": 0.34,
"lightFar": 0.62
},
"opacity": 0.44,
"pitch": 0.45,
"yaw": 0.42,
"persp": 1.1,
"dist": 2.8,
"nx": 16,
"nz": 24,
"originY": 0.34,
"ripple": {
"amp": 0,
"freqI": 0.5,
"freqK": 0.35,
"phase": 0
},
"resonance": {
"amp": 0,
"q": 14,
"axis": "ties",
"hue": 0.06,
"sat": 0.7,
"light": 0.45
},
"wind": {
"ripple": 0
}
},
"disk": {
"enabled": false,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": -0.26,
"rotation": 0,
"scale": 0.78
},
"hue": 0.06,
"sat": 0.82,
"size": 0.16,
"pressure": 0.85,
"intensity": 0.85,
"striations": 0.6,
"stain": 0.35,
"soften": 1.35
},
"bubble": {
"enabled": false,
"layerOpacity": 1,
"transform": {
"x": 0.28,
"y": -0.1,
"rotation": 10,
"scale": 0.78
},
"palette": "magentarise",
"saturation": 1.05,
"traceHue": 0,
"transparentBase": true,
"primaries": 18,
"sweepers": 5,
"cosmics": 6,
"eloss": 0.34,
"bfield": 1,
"deltaRate": 0.6,
"showBoundary": true,
"boundaryR": 0.45,
"boundaryY": 0.35,
"boundaryOpacity": 0.4,
"instrument": 0.35
},
"flora": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": -0.3,
"y": 0.5,
"rotation": 0,
"scale": 1
},
"palette": "dunelake",
"saturation": 1.05,
"count": 3,
"scale": 0.42,
"pedicels": 46,
"droop": 0.4,
"spread": 1,
"burst": 0.25,
"floretSize": 1,
"clusterW": 0.7,
"clusterH": 0.3,
"seed": "ASCLEPIAS-SLEEPING-BEAR-22"
},
"drift": {
"enabled": true,
"layerOpacity": 1,
"palette": "dunelake",
"count": 28,
"slip": 0.4,
"flutter": 0.55,
"settle": 0.06,
"tuft": 2
},
"fiduciaries": {
"enabled": true,
"layerOpacity": 1,
"label": "ASCLEPIAS SYRIACA",
"pencil": "#39312a",
"width": 1,
"arrow": true,
"corners": false,
"caption": "wind · integrated | instantaneous | advected",
"target": [
-0.3,
0.5
],
"from": [
0.35,
-0.3
]
}
};

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.9 MiB

View File

@@ -0,0 +1,240 @@
/* dune-lake-milkweed · gale · big sky, dunes low (SLEEPING-BEAR-22) */
export const composition = {
"size": 1600,
"seed": "SLEEPING-BEAR-22",
"wind": {
"seed": "BOREAS-104",
"strength": 1.25,
"scale": 1.6,
"gust": 0.4,
"time": 1.4
},
"background": {
"color": "rgb(226,216,192)",
"film": {
"opacity": 0.5,
"density": 0.5,
"seed": 8
},
"aging": {
"opacity": 0.35,
"scratches": 3,
"dust": 0.45,
"foxing": 0.5,
"seed": 5
},
"grain": {
"opacity": 0.38,
"intensity": 0.42,
"seed": 19
},
"glow": {
"strength": 0.4,
"followSun": false
},
"vignette": {
"strength": 0.32,
"mode": "radial",
"cx": 0.5,
"cy": 0.42,
"radius": 0.9,
"angle": 90,
"start": 0.5
}
},
"fieldLake": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"seed": "LIMNOS-SLEEPING-BEAR-22",
"color": {
"hueBack": 0.555,
"hueFront": 0.52,
"sat": 0.42,
"light": 0.52
},
"layers": 2,
"chaos": 0.1,
"blips": 0.2,
"mound": 0,
"horizonY": 0.45999999999999996,
"lines": 24,
"wind": {
"warp": 0.55
},
"clip": [
0,
0.42,
1,
0.64
]
},
"fieldSea": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"seed": "AEOLIAN-SLEEPING-BEAR-22",
"color": {
"hueBack": 0.09,
"hueFront": 0.07,
"sat": 0.42,
"light": 0.42
},
"layers": 3,
"chaos": 0.22,
"blips": 0.35,
"mound": 0.55,
"horizonY": 0.62,
"lines": 52,
"wind": {
"warp": 0.9
}
},
"fieldGrid": {
"enabled": false,
"layerOpacity": 1,
"pos": "over",
"style": "floor",
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"color": {
"hue": 0.5,
"hue2": 0.55,
"sat": 0.32,
"lightNear": 0.34,
"lightFar": 0.62
},
"opacity": 0.44,
"pitch": 0.45,
"yaw": 0.42,
"persp": 1.1,
"dist": 2.8,
"nx": 16,
"nz": 24,
"originY": 0.34,
"ripple": {
"amp": 0,
"freqI": 0.5,
"freqK": 0.35,
"phase": 0
},
"resonance": {
"amp": 0,
"q": 14,
"axis": "ties",
"hue": 0.06,
"sat": 0.7,
"light": 0.45
},
"wind": {
"ripple": 0
}
},
"disk": {
"enabled": false,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": -0.26,
"rotation": 0,
"scale": 0.78
},
"hue": 0.06,
"sat": 0.82,
"size": 0.16,
"pressure": 0.85,
"intensity": 0.85,
"striations": 0.6,
"stain": 0.35,
"soften": 1.35
},
"bubble": {
"enabled": false,
"layerOpacity": 1,
"transform": {
"x": 0.28,
"y": -0.1,
"rotation": 10,
"scale": 0.78
},
"palette": "magentarise",
"saturation": 1.05,
"traceHue": 0,
"transparentBase": true,
"primaries": 18,
"sweepers": 5,
"cosmics": 6,
"eloss": 0.34,
"bfield": 1,
"deltaRate": 0.6,
"showBoundary": true,
"boundaryR": 0.45,
"boundaryY": 0.35,
"boundaryOpacity": 0.4,
"instrument": 0.35
},
"flora": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": -0.34,
"y": 0.42,
"rotation": 0,
"scale": 1
},
"palette": "dunelake",
"saturation": 1.05,
"count": 3,
"scale": 0.42,
"pedicels": 46,
"droop": 0.4,
"spread": 1,
"burst": 0.25,
"floretSize": 1,
"clusterW": 0.7,
"clusterH": 0.3,
"seed": "ASCLEPIAS-SLEEPING-BEAR-22"
},
"drift": {
"enabled": true,
"layerOpacity": 1,
"palette": "dunelake",
"count": 28,
"slip": 0.4,
"flutter": 0.55,
"settle": 0.06,
"tuft": 2
},
"fiduciaries": {
"enabled": true,
"layerOpacity": 1,
"label": "ASCLEPIAS SYRIACA",
"pencil": "#39312a",
"width": 1,
"arrow": true,
"corners": false,
"caption": "wind · integrated | instantaneous | advected",
"target": [
-0.34,
0.42
],
"from": [
0.35,
-0.3
]
}
};

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.4 MiB

View File

@@ -0,0 +1,240 @@
/* dune-lake-milkweed · right-third bursting — half the florets gone to silk */
export const composition = {
"size": 1600,
"seed": "LAKEMICH-0610",
"wind": {
"seed": "BOREAS-104",
"strength": 1.25,
"scale": 1.6,
"gust": 0.4,
"time": 1.4
},
"background": {
"color": "rgb(226,216,192)",
"film": {
"opacity": 0.5,
"density": 0.5,
"seed": 8
},
"aging": {
"opacity": 0.35,
"scratches": 3,
"dust": 0.45,
"foxing": 0.5,
"seed": 5
},
"grain": {
"opacity": 0.38,
"intensity": 0.42,
"seed": 19
},
"glow": {
"strength": 0.4,
"followSun": false
},
"vignette": {
"strength": 0.32,
"mode": "radial",
"cx": 0.5,
"cy": 0.42,
"radius": 0.9,
"angle": 90,
"start": 0.5
}
},
"fieldLake": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"seed": "LIMNOS-LAKEMICH-0610",
"color": {
"hueBack": 0.555,
"hueFront": 0.52,
"sat": 0.42,
"light": 0.52
},
"layers": 2,
"chaos": 0.1,
"blips": 0.2,
"mound": 0,
"horizonY": 0.4,
"lines": 24,
"wind": {
"warp": 0.55
},
"clip": [
0,
0.36000000000000004,
1,
0.5800000000000001
]
},
"fieldSea": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"seed": "AEOLIAN-LAKEMICH-0610",
"color": {
"hueBack": 0.09,
"hueFront": 0.07,
"sat": 0.42,
"light": 0.42
},
"layers": 3,
"chaos": 0.22,
"blips": 0.35,
"mound": 0.55,
"horizonY": 0.56,
"lines": 52,
"wind": {
"warp": 0.9
}
},
"fieldGrid": {
"enabled": false,
"layerOpacity": 1,
"pos": "over",
"style": "floor",
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"color": {
"hue": 0.5,
"hue2": 0.55,
"sat": 0.32,
"lightNear": 0.34,
"lightFar": 0.62
},
"opacity": 0.44,
"pitch": 0.45,
"yaw": 0.42,
"persp": 1.1,
"dist": 2.8,
"nx": 16,
"nz": 24,
"originY": 0.34,
"ripple": {
"amp": 0,
"freqI": 0.5,
"freqK": 0.35,
"phase": 0
},
"resonance": {
"amp": 0,
"q": 14,
"axis": "ties",
"hue": 0.06,
"sat": 0.7,
"light": 0.45
},
"wind": {
"ripple": 0
}
},
"disk": {
"enabled": false,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": -0.26,
"rotation": 0,
"scale": 0.78
},
"hue": 0.06,
"sat": 0.82,
"size": 0.16,
"pressure": 0.85,
"intensity": 0.85,
"striations": 0.6,
"stain": 0.35,
"soften": 1.35
},
"bubble": {
"enabled": false,
"layerOpacity": 1,
"transform": {
"x": 0.28,
"y": -0.1,
"rotation": 10,
"scale": 0.78
},
"palette": "magentarise",
"saturation": 1.05,
"traceHue": 0,
"transparentBase": true,
"primaries": 18,
"sweepers": 5,
"cosmics": 6,
"eloss": 0.34,
"bfield": 1,
"deltaRate": 0.6,
"showBoundary": true,
"boundaryR": 0.45,
"boundaryY": 0.35,
"boundaryOpacity": 0.4,
"instrument": 0.35
},
"flora": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0.42,
"y": 0.44,
"rotation": 0,
"scale": 1
},
"palette": "dunelake",
"saturation": 1.05,
"count": 3,
"scale": 0.42,
"pedicels": 46,
"droop": 0.4,
"spread": 1,
"burst": 0.55,
"floretSize": 1,
"clusterW": 0.7,
"clusterH": 0.3,
"seed": "ASCLEPIAS-LAKEMICH-0610"
},
"drift": {
"enabled": true,
"layerOpacity": 1,
"palette": "dunelake",
"count": 40,
"slip": 0.4,
"flutter": 0.55,
"settle": 0.06,
"tuft": 2
},
"fiduciaries": {
"enabled": true,
"layerOpacity": 1,
"label": "ASCLEPIAS SYRIACA",
"pencil": "#39312a",
"width": 1,
"arrow": true,
"corners": false,
"caption": "release fraction 0.55",
"target": [
0.42,
0.44
],
"from": [
0.35,
-0.3
]
}
};

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.0 MiB

View File

@@ -0,0 +1,240 @@
/* dune-lake-milkweed · umbels closed — the vertex before the decay */
export const composition = {
"size": 1600,
"seed": "LAKEMICH-0610",
"wind": {
"seed": "ZEPHYR-011",
"strength": 0.55,
"scale": 1.6,
"gust": 0.4,
"time": 1.4
},
"background": {
"color": "rgb(226,216,192)",
"film": {
"opacity": 0.5,
"density": 0.5,
"seed": 8
},
"aging": {
"opacity": 0.35,
"scratches": 3,
"dust": 0.45,
"foxing": 0.5,
"seed": 5
},
"grain": {
"opacity": 0.38,
"intensity": 0.42,
"seed": 19
},
"glow": {
"strength": 0.4,
"followSun": false
},
"vignette": {
"strength": 0.32,
"mode": "radial",
"cx": 0.5,
"cy": 0.42,
"radius": 0.9,
"angle": 90,
"start": 0.5
}
},
"fieldLake": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"seed": "LIMNOS-LAKEMICH-0610",
"color": {
"hueBack": 0.555,
"hueFront": 0.52,
"sat": 0.42,
"light": 0.52
},
"layers": 2,
"chaos": 0.1,
"blips": 0.2,
"mound": 0,
"horizonY": 0.4,
"lines": 24,
"wind": {
"warp": 0.55
},
"clip": [
0,
0.36000000000000004,
1,
0.5800000000000001
]
},
"fieldSea": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"seed": "AEOLIAN-LAKEMICH-0610",
"color": {
"hueBack": 0.09,
"hueFront": 0.07,
"sat": 0.42,
"light": 0.42
},
"layers": 3,
"chaos": 0.22,
"blips": 0.35,
"mound": 0.55,
"horizonY": 0.56,
"lines": 52,
"wind": {
"warp": 0.9
}
},
"fieldGrid": {
"enabled": false,
"layerOpacity": 1,
"pos": "over",
"style": "floor",
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"color": {
"hue": 0.5,
"hue2": 0.55,
"sat": 0.32,
"lightNear": 0.34,
"lightFar": 0.62
},
"opacity": 0.44,
"pitch": 0.45,
"yaw": 0.42,
"persp": 1.1,
"dist": 2.8,
"nx": 16,
"nz": 24,
"originY": 0.34,
"ripple": {
"amp": 0,
"freqI": 0.5,
"freqK": 0.35,
"phase": 0
},
"resonance": {
"amp": 0,
"q": 14,
"axis": "ties",
"hue": 0.06,
"sat": 0.7,
"light": 0.45
},
"wind": {
"ripple": 0
}
},
"disk": {
"enabled": false,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": -0.26,
"rotation": 0,
"scale": 0.78
},
"hue": 0.06,
"sat": 0.82,
"size": 0.16,
"pressure": 0.85,
"intensity": 0.85,
"striations": 0.6,
"stain": 0.35,
"soften": 1.35
},
"bubble": {
"enabled": false,
"layerOpacity": 1,
"transform": {
"x": 0.28,
"y": -0.1,
"rotation": 10,
"scale": 0.78
},
"palette": "magentarise",
"saturation": 1.05,
"traceHue": 0,
"transparentBase": true,
"primaries": 18,
"sweepers": 5,
"cosmics": 6,
"eloss": 0.34,
"bfield": 1,
"deltaRate": 0.6,
"showBoundary": true,
"boundaryR": 0.45,
"boundaryY": 0.35,
"boundaryOpacity": 0.4,
"instrument": 0.35
},
"flora": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": -0.5,
"y": 0.38,
"rotation": 0,
"scale": 1
},
"palette": "dunelake",
"saturation": 1.05,
"count": 2,
"scale": 0.42,
"pedicels": 46,
"droop": 0.4,
"spread": 1,
"burst": 0,
"floretSize": 1,
"clusterW": 0.7,
"clusterH": 0.3,
"seed": "ASCLEPIAS-LAKEMICH-0610"
},
"drift": {
"enabled": false,
"layerOpacity": 1,
"palette": "dunelake",
"count": 28,
"slip": 0.4,
"flutter": 0.55,
"settle": 0.06,
"tuft": 2
},
"fiduciaries": {
"enabled": true,
"layerOpacity": 1,
"label": "ASCLEPIAS SYRIACA",
"pencil": "#39312a",
"width": 1,
"arrow": true,
"corners": false,
"caption": "pre-release · all florets held",
"target": [
-0.5,
0.38
],
"from": [
0.35,
-0.3
]
}
};

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 4.7 MiB

View File

@@ -0,0 +1,240 @@
/* dune-lake-milkweed · off-corner full burst — seeds streaming across the water */
export const composition = {
"size": 1600,
"seed": "SLEEPING-BEAR-22",
"wind": {
"seed": "BOREAS-104",
"strength": 1.4,
"scale": 1.6,
"gust": 0.4,
"time": 1.4
},
"background": {
"color": "rgb(226,216,192)",
"film": {
"opacity": 0.5,
"density": 0.5,
"seed": 8
},
"aging": {
"opacity": 0.35,
"scratches": 3,
"dust": 0.45,
"foxing": 0.5,
"seed": 5
},
"grain": {
"opacity": 0.38,
"intensity": 0.42,
"seed": 19
},
"glow": {
"strength": 0.4,
"followSun": false
},
"vignette": {
"strength": 0.32,
"mode": "radial",
"cx": 0.5,
"cy": 0.42,
"radius": 0.9,
"angle": 90,
"start": 0.5
}
},
"fieldLake": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"seed": "LIMNOS-SLEEPING-BEAR-22",
"color": {
"hueBack": 0.555,
"hueFront": 0.52,
"sat": 0.42,
"light": 0.52
},
"layers": 2,
"chaos": 0.1,
"blips": 0.2,
"mound": 0,
"horizonY": 0.33999999999999997,
"lines": 24,
"wind": {
"warp": 0.55
},
"clip": [
0,
0.3,
1,
0.52
]
},
"fieldSea": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"seed": "AEOLIAN-SLEEPING-BEAR-22",
"color": {
"hueBack": 0.09,
"hueFront": 0.07,
"sat": 0.42,
"light": 0.42
},
"layers": 3,
"chaos": 0.22,
"blips": 0.35,
"mound": 0.55,
"horizonY": 0.5,
"lines": 52,
"wind": {
"warp": 0.9
}
},
"fieldGrid": {
"enabled": false,
"layerOpacity": 1,
"pos": "over",
"style": "floor",
"transform": {
"x": 0,
"y": 0,
"rotation": 0,
"scale": 1
},
"color": {
"hue": 0.5,
"hue2": 0.55,
"sat": 0.32,
"lightNear": 0.34,
"lightFar": 0.62
},
"opacity": 0.44,
"pitch": 0.45,
"yaw": 0.42,
"persp": 1.1,
"dist": 2.8,
"nx": 16,
"nz": 24,
"originY": 0.34,
"ripple": {
"amp": 0,
"freqI": 0.5,
"freqK": 0.35,
"phase": 0
},
"resonance": {
"amp": 0,
"q": 14,
"axis": "ties",
"hue": 0.06,
"sat": 0.7,
"light": 0.45
},
"wind": {
"ripple": 0
}
},
"disk": {
"enabled": false,
"layerOpacity": 1,
"transform": {
"x": 0,
"y": -0.26,
"rotation": 0,
"scale": 0.78
},
"hue": 0.06,
"sat": 0.82,
"size": 0.16,
"pressure": 0.85,
"intensity": 0.85,
"striations": 0.6,
"stain": 0.35,
"soften": 1.35
},
"bubble": {
"enabled": false,
"layerOpacity": 1,
"transform": {
"x": 0.28,
"y": -0.1,
"rotation": 10,
"scale": 0.78
},
"palette": "magentarise",
"saturation": 1.05,
"traceHue": 0,
"transparentBase": true,
"primaries": 18,
"sweepers": 5,
"cosmics": 6,
"eloss": 0.34,
"bfield": 1,
"deltaRate": 0.6,
"showBoundary": true,
"boundaryR": 0.45,
"boundaryY": 0.35,
"boundaryOpacity": 0.4,
"instrument": 0.35
},
"flora": {
"enabled": true,
"layerOpacity": 1,
"transform": {
"x": -0.78,
"y": 0.62,
"rotation": 0,
"scale": 1
},
"palette": "dunelake",
"saturation": 1.05,
"count": 2,
"scale": 0.5,
"pedicels": 46,
"droop": 0.4,
"spread": 1,
"burst": 1,
"floretSize": 1,
"clusterW": 0.7,
"clusterH": 0.3,
"seed": "ASCLEPIAS-SLEEPING-BEAR-22"
},
"drift": {
"enabled": true,
"layerOpacity": 1,
"palette": "dunelake",
"count": 64,
"slip": 0.5,
"flutter": 0.55,
"settle": 0.06,
"tuft": 2
},
"fiduciaries": {
"enabled": true,
"layerOpacity": 1,
"label": "ASCLEPIAS SYRIACA",
"pencil": "#39312a",
"width": 1,
"arrow": true,
"corners": false,
"caption": "all silk · the field made visible",
"target": [
-0.78,
0.62
],
"from": [
0.4,
-0.35
]
}
};

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 MiB

View File

@@ -6,18 +6,30 @@
scale about that centre). Groups:
background — base colour + film/diffusion + aging + grain
fieldLake — Field 0: a second carpet band (water — instantaneous wind)
fieldSea — Field 1: the ridgeline vacuum carpet (N plate sheets)
fieldGrid — Field 2: the rippled perspective depth grid (2 sheets)
disk — the shock disk / "sun" (own centre/rotation/scale)
bubble — the particle event / tracks (own centre/rotation/scale)
flora — milkweed umbels (particle-vertex flower heads)
drift — released seeds advected by the shared wind field
fiduciaries — frame-level "No 001" + arrow (does NOT scale w/ event)
comp.wind = { seed, strength, scale, drift, gust, time } defines ONE
shared wind field; fieldSea warps by its time-integral (dune memory),
fieldLake/fieldGrid ripple by its instantaneous value, and drift
advects seeds through it — three detectors of the same invisible field.
renderComposition(comp, size) → one composited SVG string.
============================================================ */
import { carpetSVG } from '../qft/carpet.js';
import { perspectiveGridSVG, perspFloorSVG } from '../qft/perspgrid.js';
import { fiduciarySVG } from './fiduciary.js';
import { generateScene } from '../scene/scene.js';
import { generateUmbel } from '../scene/umbel.js';
import { generateDrift } from '../scene/drift.js';
import { makeWindField } from '../field/wind.js';
import { makeRng, range } from '../rng.js';
import { renderSVG } from '../render/svgVector.js';
import { paramsFromSeed } from '../scene/params.js';
import { GROUPS, TOGGLES, FIXED } from '../ui/controls.js';
@@ -128,6 +140,65 @@ function bubbleLayer(W, seed, b) {
return renderSVG(generateScene(p), p, W);
}
// ---------- wind / flora / drift ----------
// ONE wind field per composition (seeded independently of the scene so the
// weather can be re-rolled without reshuffling the flora).
const windFor = (comp, seed) => makeWindField((comp.wind && comp.wind.seed) || seed, comp.wind || {});
/* Generate the flora's umbels — tracks for the flora layer, release points for
the drift layer. Both builders call this with identical inputs, so they agree
exactly on which pedicels have gone to seed (no cross-group state). */
function floraData(seed, f) {
const rng = makeRng(String(f.seed || seed), 'umbel');
const defs = f.umbels || Array.from({ length: f.count ?? 1 }, () => ({
x: range(rng, -0.5, 0.5) * (f.clusterW ?? 0.9),
y: range(rng, -0.5, 0.5) * (f.clusterH ?? 0.5),
scale: (f.scale ?? 0.5) * range(rng, 0.8, 1.2),
}));
const tracks = [], seeds = [];
for (const d of defs) {
const u = generateUmbel({
pedicels: f.pedicels ?? 48, droop: f.droop ?? 0.35, spread: f.spread ?? 1,
burst: f.burst ?? 0, floretSize: f.floretSize ?? 1, scale: f.scale ?? 0.5, ...d,
}, rng);
tracks.push(...u.tracks); seeds.push(...u.seeds);
}
return { tracks, seeds };
}
function floraParams(seed, cfg) {
const p = bcParamsBase(seed);
Object.assign(p, {
palette: cfg.palette ?? 'dunelake', saturation: cfg.saturation ?? 1.05, traceHue: cfg.traceHue ?? 0,
showBoundary: false, instrument: 0, emit: ['bubble'],
});
return p;
}
function floraLayer(W, seed, f) {
const { tracks } = floraData(seed, f);
return renderSVG({ tracks }, floraParams(seed, f), W);
}
function driftLayer(W, seed, comp) {
const dcfg = comp.drift, f = comp.flora || {};
const { seeds } = floraData(seed, f);
if (!seeds.length) return null;
// seeds are born in the flora layer's LOCAL frame; carry them through the
// flora transform so they ride ONE frame-wide wind (the drift layer itself
// composites untransformed — the whole point of the shared field)
const t = f.transform || {};
const rot = (t.rotation ?? 0) * Math.PI / 180, sc = t.scale ?? 1;
const cr = Math.cos(rot), sr = Math.sin(rot);
const fSeeds = seeds.map((s) => ({
x: (s.x * cr - s.y * sr) * sc + (t.x ?? 0),
y: (s.x * sr + s.y * cr) * sc + (t.y ?? 0),
theta: s.theta + rot,
}));
const wind = windFor(comp, seed);
const rng = makeRng(String(dcfg.seed || f.seed || seed), 'drift');
const tracks = generateDrift(dcfg, wind, fSeeds, rng);
if (!tracks.length) return null;
return renderSVG({ tracks }, floraParams(seed, { palette: dcfg.palette ?? f.palette, saturation: dcfg.saturation ?? f.saturation }), W);
}
// ---------- field decks ----------
function seaSheets(W, f) {
const n = f.layers ?? 3;
@@ -137,8 +208,9 @@ function seaSheets(W, f) {
const lightNear = lite != null ? lite : 0.33;
const lightFar = lite != null ? Math.min(0.97, lite + 0.18) : 0.56;
const base = {
mode: 'plate', rows: f.lines ?? 46, horizon: f.horizonY ?? 0.36, wFar: 0.58, wNear: 0.7,
mode: 'plate', rows: f.lines ?? 46, horizon: f.horizonY ?? 0.36, wFar: f.wFar ?? 0.58, wNear: f.wNear ?? 0.7,
overlap: f.overlap ?? 1.7, mound: f.mound ?? 0.3, sat: (f.color?.sat) ?? 0.6, lightNear, lightFar, blips: f.blips ?? 0.7,
warpFn: f.warpFn ?? null, phaseFn: f.phaseFn ?? null, modesFn: f.modesFn ?? null,
};
const hueBack = f.color?.hueBack ?? 0.54, hueFront = f.color?.hueFront ?? 0.47;
const strokes = [{ near: 2.6, far: 1.0 }, { near: 1.6, far: 0.6 }, { near: 1.0, far: 0.38 }];
@@ -180,7 +252,8 @@ function gridSheets(W, f) {
mode: 'plate', nx: f.nx ?? 16, nz: f.nz ?? 24,
pitch: f.pitch ?? 28 * D, yaw: f.yaw ?? 0, roll: f.roll ?? 0, persp: f.persp ?? 1.0,
dist: f.dist ?? 3.0, zShift: f.zShift ?? 0, scale: f.scale ?? 1, originX: f.originX ?? 0, originY: f.originY ?? 0.34,
rippleAmp: f.ripple?.amp ?? 0, rippleFreqI: f.ripple?.freqI ?? 0.5, rippleFreqK: f.ripple?.freqK ?? 0.35, ripplePhase: f.ripple?.phase ?? 0, ...color,
rippleAmp: f.ripple?.amp ?? 0, rippleFreqI: f.ripple?.freqI ?? 0.5, rippleFreqK: f.ripple?.freqK ?? 0.35, ripplePhase: f.ripple?.phase ?? 0,
heightFn: f.heightFn ?? null, ...color,
};
const r = f.resonance || {};
const res = { resAmp: r.amp ?? 0, resQ: r.q ?? 14, resAxis: r.axis ?? 'ties', resHue: r.hue ?? 0.06, resSat: r.sat ?? 0.7, resLight: r.light ?? 0.45 };
@@ -189,8 +262,93 @@ function gridSheets(W, f) {
return [{ href: uri(back), blur: blur[0] ?? 1.1, opacity: op }, { href: uri(front), blur: blur[1] ?? 0, opacity: op + 0.16 }];
}
// The 6 craft groups, in conceptual order.
export const COMPOSITION_GROUPS = ['background', 'fieldSea', 'fieldGrid', 'disk', 'bubble', 'fiduciaries'];
/* Attach wind closures to a carpet group config. mode 'integrated' = the
field's time integral (dune memory) · 'instant' = its live value (water). */
function windCarpet(comp, seed, f, mode) {
const wcfg = f.wind || {};
const warpK = wcfg.warp ?? 0, phaseK = wcfg.phase ?? 0;
if ((!warpK && !phaseK) || !comp.wind) return f;
const wind = windFor(comp, seed);
const T = comp.wind.time ?? 1;
const yTop = (f.horizonY ?? 0.36) * 2 - 1; // rows live horizon→bottom
const m = wcfg.mode || mode;
const warpFn = warpK ? (t, d) => {
const x = t * 2 - 1, y = yTop + d * (1 - yTop); // row sample in frame coords
return m === 'instant'
? warpK * wind.scalar(x, y, T)
: warpK * wind.displacement(x, y, T).dy; // what the sand remembers
} : null;
const phaseFn = phaseK ? (t, r) => phaseK * wind.scalar(t * 2 - 1, r * 0.04, T) : null;
return { ...f, warpFn, phaseFn };
}
/* The craft groups as a REGISTRY, in conceptual order. Each builder receives
(comp, W, push, seed) and pushes z-ordered pieces. Adding a new layer kind
(the next discovery plate) = adding one entry here + a schema default. */
const GROUP_BUILDERS = {
background(comp, W, push) {
const b = comp.background || {};
const rgb = parseRGB(b.color);
push(0, { rect: b.color || 'rgb(229,222,203)' });
if (b.glow && b.glow.strength) {
// glow centre follows the sun (disk) unless followSun:false
const dt = comp.disk?.transform || {};
const fs = b.glow.followSun !== false && comp.disk?.enabled !== false;
const gcx = fs ? (((dt.x ?? 0) + 1) / 2) : 0.5, gcy = fs ? (((dt.y ?? 0) + 1) / 2) : 0.44;
push(0.6, { href: uri(glowSVG(W, rgb, b.glow.strength, gcx, gcy)), opacity: 1 });
}
if (b.film && b.film.opacity !== 0) push(1, { href: uri(filmSVG(W, b.film)), opacity: b.film.opacity ?? 0.6 });
if (b.vignette && b.vignette.strength) push(90, { href: uri(vignetteSVG(W, b.vignette)), opacity: 1, blend: 'multiply' }); // edge darkening (over the piece)
if (b.aging && b.aging.opacity !== 0) push(80, { href: uri(agingSVG(W, b.aging)), opacity: b.aging.opacity ?? 0.55, blend: 'multiply' });
if (b.grain && b.grain.opacity !== 0) push(95, { href: uri(grainSVG(W, { seed: b.grain.seed, tone: b.grain.tone, intensity: b.grain.intensity })), opacity: b.grain.opacity ?? 0.42, blend: 'multiply' });
},
fieldLake(comp, W, push, seed) {
const f = comp.fieldLake; if (!f || f.enabled === false) return;
seaSheets(W, windCarpet(comp, seed, f, 'instant')).forEach((s, i) => push(7 + i, { href: s.href, blur: s.blur, opacity: s.opacity, transform: f.transform, clip: f.clip }));
},
fieldSea(comp, W, push, seed) {
const f = comp.fieldSea; if (!f || f.enabled === false) return;
seaSheets(W, windCarpet(comp, seed, f, 'integrated')).forEach((s, i) => push(10 + i, { href: s.href, blur: s.blur, opacity: s.opacity, transform: f.transform, clip: f.clip }));
},
fieldGrid(comp, W, push, seed) {
let f = comp.fieldGrid; if (!f || f.enabled === false) return;
const rip = f.wind?.ripple ?? 0;
if (rip && comp.wind) {
const wind = windFor(comp, seed);
const T = comp.wind.time ?? 1, nx = f.nx ?? 16, nz = f.nz ?? 24;
f = { ...f, heightFn: (i, k) => rip * wind.scalar(i / nx, k / nz, T) };
}
const z0 = f.pos === 'behind' ? 5 : 25;
gridSheets(W, f).forEach((s, i) => push(z0 + i, { href: s.href, blur: s.blur, opacity: s.opacity, transform: f.transform, clip: f.clip }));
},
disk(comp, W, push, seed) {
const d = comp.disk; if (!d || d.enabled === false) return;
push(40, { href: uri(diskLayer(W, seed, d)), opacity: d.opacity ?? 1, transform: d.transform });
},
bubble(comp, W, push, seed) {
const b = comp.bubble; if (!b || b.enabled === false) return;
// transparentBase (default true): float as an object (normal blend). When
// false, the event keeps its paper base and composites via multiply.
const tb = b.transparentBase !== false;
push(50, { href: uri(bubbleLayer(W, seed, b)), opacity: b.opacity ?? 1, transform: b.transform, blend: tb ? undefined : 'multiply' });
},
flora(comp, W, push, seed) {
const f = comp.flora; if (!f || f.enabled === false) return;
push(55, { href: uri(floraLayer(W, seed, f)), opacity: f.opacity ?? 1, transform: f.transform, clip: f.clip });
},
drift(comp, W, push, seed) {
const d = comp.drift; if (!d || d.enabled === false) return;
const svg = driftLayer(W, seed, comp);
if (svg) push(60, { href: uri(svg), opacity: d.opacity ?? 1, transform: d.transform, clip: d.clip });
},
fiduciaries(comp, W, push, seed) {
const fd = comp.fiduciaries; if (!fd || fd.enabled === false) return;
const target = fd.target || [comp.bubble?.transform?.x ?? 0, comp.bubble?.transform?.y ?? 0];
push(85, { href: uri(fiduciarySVG(W, { ...fd, target, seed })), opacity: 1, transform: fd.transform });
},
};
export const COMPOSITION_GROUPS = Object.keys(GROUP_BUILDERS);
/* Render ONE group to a set of draw "pieces" (z-ordered). The UI caches these
per group so editing one layer only re-renders that layer. Each piece:
@@ -199,55 +357,8 @@ export function buildGroupPieces(group, comp, W) {
const seed = comp.seed || 'MESON-5113';
const out = [];
const push = (z, o) => out.push({ z, ...o });
switch (group) {
case 'background': {
const b = comp.background || {};
const rgb = parseRGB(b.color);
push(0, { rect: b.color || 'rgb(229,222,203)' });
if (b.glow && b.glow.strength) {
// glow centre follows the sun (disk) unless followSun:false
const dt = comp.disk?.transform || {};
const fs = b.glow.followSun !== false && comp.disk?.enabled !== false;
const gcx = fs ? (((dt.x ?? 0) + 1) / 2) : 0.5, gcy = fs ? (((dt.y ?? 0) + 1) / 2) : 0.44;
push(0.6, { href: uri(glowSVG(W, rgb, b.glow.strength, gcx, gcy)), opacity: 1 });
}
if (b.film && b.film.opacity !== 0) push(1, { href: uri(filmSVG(W, b.film)), opacity: b.film.opacity ?? 0.6 });
if (b.vignette && b.vignette.strength) push(90, { href: uri(vignetteSVG(W, b.vignette)), opacity: 1, blend: 'multiply' }); // edge darkening (over the piece)
if (b.aging && b.aging.opacity !== 0) push(80, { href: uri(agingSVG(W, b.aging)), opacity: b.aging.opacity ?? 0.55, blend: 'multiply' });
if (b.grain && b.grain.opacity !== 0) push(95, { href: uri(grainSVG(W, { seed: b.grain.seed, tone: b.grain.tone, intensity: b.grain.intensity })), opacity: b.grain.opacity ?? 0.42, blend: 'multiply' });
break;
}
case 'fieldSea': {
const f = comp.fieldSea; if (!f || f.enabled === false) break;
seaSheets(W, f).forEach((s, i) => push(10 + i, { href: s.href, blur: s.blur, opacity: s.opacity, transform: f.transform, clip: f.clip }));
break;
}
case 'fieldGrid': {
const f = comp.fieldGrid; if (!f || f.enabled === false) break;
const z0 = f.pos === 'behind' ? 5 : 25;
gridSheets(W, f).forEach((s, i) => push(z0 + i, { href: s.href, blur: s.blur, opacity: s.opacity, transform: f.transform, clip: f.clip }));
break;
}
case 'disk': {
const d = comp.disk; if (!d || d.enabled === false) break;
push(40, { href: uri(diskLayer(W, seed, d)), opacity: d.opacity ?? 1, transform: d.transform });
break;
}
case 'bubble': {
const b = comp.bubble; if (!b || b.enabled === false) break;
// transparentBase (default true): float as an object (normal blend). When
// false, the event keeps its paper base and composites via multiply.
const tb = b.transparentBase !== false;
push(50, { href: uri(bubbleLayer(W, seed, b)), opacity: b.opacity ?? 1, transform: b.transform, blend: tb ? undefined : 'multiply' });
break;
}
case 'fiduciaries': {
const fd = comp.fiduciaries; if (!fd || fd.enabled === false) break;
const target = fd.target || [comp.bubble?.transform?.x ?? 0, comp.bubble?.transform?.y ?? 0];
push(85, { href: uri(fiduciarySVG(W, { ...fd, target, seed })), opacity: 1, transform: fd.transform });
break;
}
}
const builder = GROUP_BUILDERS[group];
if (builder) builder(comp, W, push, seed);
// common per-layer OPACITY: multiply every piece's opacity (baseOpacity kept so
// the UI can re-apply cheaply without regenerating the layer)
const lo = comp[group] && comp[group].layerOpacity;

View File

@@ -8,6 +8,10 @@
============================================================ */
export const DEFAULT_COMPOSITION = {
size: 1600, seed: 'MESON-5113',
// ONE shared wind field (see src/field/wind.js). Consumed by fieldSea
// (time-integrated warp), fieldLake/fieldGrid (instantaneous ripple) and
// drift (seed advection). Groups opt in via their own `wind` sub-config.
wind: { seed: 'ZEPHYR-001', strength: 1, scale: 1.6, gust: 0.35, time: 1 },
background: {
color: 'rgb(229,222,203)',
film: { opacity: 0.6, density: 0.5, seed: 8 },
@@ -16,16 +20,24 @@ export const DEFAULT_COMPOSITION = {
glow: { strength: 0.55, followSun: true }, // lit-page glow; centre tracks the disk
vignette: { strength: 0.4, mode: 'radial', cx: 0.5, cy: 0.48, radius: 0.75, angle: 90, start: 0.5 },
},
fieldLake: {
enabled: false, layerOpacity: 1, transform: { x: 0, y: 0, rotation: 0, scale: 1 }, seed: 'LIMNOS-001',
color: { hueBack: 0.55, hueFront: 0.56, sat: 0.45, light: 0.5 },
layers: 2, chaos: 0.12, blips: 0.25, mound: 0, horizonY: 0.36, lines: 26,
wind: { warp: 0.5 }, // instantaneous — the water shows the wind live
},
fieldSea: {
enabled: true, layerOpacity: 1, transform: { x: 0, y: 0, rotation: 0, scale: 1 }, seed: 'VACUUM-5113',
color: { hueBack: 0.54, hueFront: 0.47, sat: 0.6, light: 0.36 }, // light > ~0.6 → waves lighter than ground
layers: 3, chaos: 0.3, blips: 0.7, mound: 0.3, horizonY: 0.36, lines: 46,
wind: { warp: 0 }, // time-integrated — what the sand remembers
},
fieldGrid: {
enabled: true, layerOpacity: 1, pos: 'over', style: 'floor', transform: { x: 0, y: 0, rotation: 0, scale: 1 },
color: { hue: 0.5, hue2: 0.55, sat: 0.32, lightNear: 0.34, lightFar: 0.62 }, opacity: 0.44,
pitch: 0.45, yaw: 0.42, persp: 1.1, dist: 2.8, nx: 16, nz: 24, originY: 0.34, ripple: { amp: 0, freqI: 0.5, freqK: 0.35, phase: 0 },
resonance: { amp: 0, q: 14, axis: 'ties', hue: 0.06, sat: 0.7, light: 0.45 },
wind: { ripple: 0 }, // instantaneous wind as floor height
},
disk: {
enabled: true, layerOpacity: 1, transform: { x: 0, y: -0.26, rotation: 0, scale: 0.78 },
@@ -37,6 +49,16 @@ export const DEFAULT_COMPOSITION = {
primaries: 18, sweepers: 5, cosmics: 6, eloss: 0.34, bfield: 1.0, deltaRate: 0.6,
showBoundary: true, boundaryR: 0.45, boundaryY: 0.35, boundaryOpacity: 0.4, instrument: 0.35,
},
flora: {
enabled: false, layerOpacity: 1, transform: { x: 0, y: 0, rotation: 0, scale: 1 },
palette: 'dunelake', saturation: 1.05, count: 1, scale: 0.5,
pedicels: 48, droop: 0.35, spread: 1, burst: 0, floretSize: 1,
clusterW: 0.9, clusterH: 0.5, // placement spread when count > 1 (no explicit umbels[])
},
drift: {
enabled: false, layerOpacity: 1,
palette: 'dunelake', count: 24, slip: 0.35, flutter: 0.5, settle: 0.06, tuft: 2,
},
fiduciaries: {
enabled: true, layerOpacity: 1, label: 'auto', pencil: '#39312a', width: 1.0, arrow: true, corners: false, caption: '',
},
@@ -66,6 +88,21 @@ export const GROUPS_SCHEMA = [
R('vignette.angle', 'Vignette angle°', 0, 360, 1),
],
},
{
id: 'fieldLake', label: 'Field · Lake', transform: true, enable: true, controls: [
R('color.hueBack', 'Hue · far', 0, 1, 0.005),
R('color.hueFront', 'Hue · near', 0, 1, 0.005),
R('color.sat', 'Saturation', 0, 1, 0.01),
R('color.light', 'Wave luminosity', 0, 1, 0.01),
R('layers', 'Plate layers', 1, 3, 1),
R('chaos', 'Chaos', 0, 1, 0.01),
R('blips', 'Blips', 0, 2, 0.05),
R('horizonY', 'Horizon Y', 0.2, 0.6, 0.01),
R('lines', '# lines', 8, 80, 1),
R('wind.warp', 'Wind ripple', 0, 2, 0.02),
{ path: 'seed', label: 'Seed', type: 'text' },
],
},
{
id: 'fieldSea', label: 'Field · Sea', transform: true, enable: true, controls: [
R('color.hueBack', 'Hue · far', 0, 1, 0.005),
@@ -78,6 +115,7 @@ export const GROUPS_SCHEMA = [
R('mound', 'Mound', 0, 1, 0.01),
R('horizonY', 'Horizon Y', 0.2, 0.6, 0.01),
R('lines', '# lines', 16, 80, 1),
R('wind.warp', 'Wind warp (dune memory)', 0, 2, 0.02),
{ path: 'seed', label: 'Seed', type: 'text' },
],
},
@@ -96,6 +134,7 @@ export const GROUPS_SCHEMA = [
R('color.hue', 'Hue', 0, 1, 0.005),
R('color.sat', 'Saturation', 0, 1, 0.01),
R('ripple.amp', 'Floor ripple', 0, 2, 0.02),
R('wind.ripple', 'Wind ripple', 0, 2, 0.02),
R('resonance.amp', 'Resonance (high-Q)', 0, 1, 0.01),
R('resonance.q', 'Resonance Q', 2, 40, 1),
R('resonance.hue', 'Resonance hue', 0, 1, 0.005),
@@ -131,6 +170,30 @@ export const GROUPS_SCHEMA = [
R('instrument', 'Structural geometry', 0, 1, 0.01),
],
},
{
id: 'flora', label: 'Flora · milkweed', transform: true, enable: true, controls: [
{ path: 'palette', label: 'Palette', type: 'select', options: ['dunelake', 'magentarise', 'mono', 'kind', 'kindrise', 'kindlife', 'lifecycle'] },
R('saturation', 'Saturation', 0, 1.5, 0.01),
R('count', 'Umbels', 1, 7, 1),
R('scale', 'Head size', 0.15, 1.2, 0.01),
R('pedicels', 'Pedicels', 8, 80, 1),
R('droop', 'Droop', 0, 1, 0.01),
R('spread', 'Fan looseness', 0, 2, 0.01),
R('burst', 'Burst (gone to seed)', 0, 1, 0.01),
R('floretSize', 'Floret size', 0.3, 2.5, 0.01),
{ path: 'seed', label: 'Seed', type: 'text' },
],
},
{
id: 'drift', label: 'Drift · seeds', enable: true, controls: [
R('count', 'Seeds (cap)', 0, 80, 1),
R('slip', 'Launch slip', 0, 1, 0.01),
R('flutter', 'Coma flutter', 0, 1.5, 0.01),
R('settle', 'Settle speed', 0, 0.3, 0.005),
R('tuft', 'Tuft silks', 0, 5, 1),
R('layerOpacity', 'Opacity', 0, 1, 0.01),
],
},
{
id: 'fiduciaries', label: 'Fiduciaries', transform: true, enable: true, controls: [
{ path: 'label', label: 'Label', type: 'text' },

View File

@@ -143,6 +143,26 @@ export const TEMPLATES = [
fiduciaries: { label: 'auto' },
},
},
// 9 — DUNE LAKE · first of the HIDDEN-REALITIES series: natural subjects as
// instrument photographs. One wind field, three detectors — dunes (time-
// integrated), water (instantaneous), milkweed seed drift (advected); the
// umbel is an interaction vertex grown botanically. Future series entries:
// DNA/Photo 51 (helical heightFn rails), chlorophyll (porphyrin rosette,
// green-paper inverted palette), CMB (power-spectrum modesFn carpet).
{
id: 'dune-lake', name: 'Dunes on a Great Lake', source: 'Hidden Realities', series: 'hidden-realities', reps: 3,
seeds: ['LAKEMICH-0610', 'SLEEPING-BEAR-22', 'CRITICALDUNE-7'],
over: {
wind: { seed: 'ZEPHYR-011', strength: 0.8, scale: 1.6, gust: 0.4, time: 1.4 },
background: { color: 'rgb(226,216,192)', glow: { strength: 0.4, followSun: false }, vignette: { strength: 0.32, cy: 0.42, radius: 0.9 } },
fieldLake: { enabled: true, color: { hueBack: 0.555, hueFront: 0.52, sat: 0.42, light: 0.52 }, layers: 2, chaos: 0.1, blips: 0.2, mound: 0, lines: 24, horizonY: 0.4, wind: { warp: 0.55 }, clip: [0, 0.36, 1, 0.58] },
fieldSea: { color: { hueBack: 0.09, hueFront: 0.07, sat: 0.42, light: 0.42 }, chaos: 0.22, blips: 0.35, mound: 0.55, horizonY: 0.56, lines: 52, wind: { warp: 0.9 } },
fieldGrid: { enabled: false }, disk: { enabled: false }, bubble: { enabled: false },
flora: { enabled: true, seed: 'ASCLEPIAS-1', palette: 'dunelake', count: 3, scale: 0.42, pedicels: 46, droop: 0.4, burst: 0.3, clusterW: 0.7, clusterH: 0.3, transform: { x: -0.34, y: 0.42, rotation: 0, scale: 1 } },
drift: { enabled: true, count: 28, slip: 0.4, flutter: 0.55, tuft: 2 },
fiduciaries: { label: 'ASCLEPIAS SYRIACA', target: [-0.34, 0.42], from: [0.35, -0.3] },
},
},
];
// build the composition for a template at variation index i

72
src/field/wind.js Normal file
View File

@@ -0,0 +1,72 @@
/* ============================================================
field/wind.js — the SHARED WIND FIELD: one invisible field,
three detectors. The same seeded flow is sampled three ways:
displacement(x,y,T) time-integrated — what the SAND remembers
(warps the dune ridgelines)
vec(x,y,t) / scalar instantaneous — what the WATER shows
(drives lake ripple phase)
vec stepped advected — what the SEEDS trace
(milkweed drift = test particles)
Divergence-free curl noise from a streamfunction ψ(x,y,t) built
as a sum of K seeded sinusoidal modes plus a mean drift, so the
velocity has a closed-form time integral (no numerics, fully
deterministic). Coordinates are frame-normalized 1..1; typical
|vec| ≈ strength.
============================================================ */
import { makeRng, range } from '../rng.js';
export function makeWindField(seed, opts = {}) {
const o = Object.assign({
modes: 6, scale: 1.6, strength: 1,
drift: [0.6, 0.05], // prevailing wind (mean velocity, frame units)
gust: 0.35, // how much the modes stir vs. the mean drift
}, opts);
const rng = makeRng(seed, 'wind');
const modes = [];
for (let m = 0; m < o.modes; m++) {
const ang = range(rng, 0, Math.PI * 2);
const k = range(rng, 0.6, 2.2) * o.scale; // low wavenumber: long swells
modes.push({
kx: Math.cos(ang) * k, ky: Math.sin(ang) * k,
a: range(rng, 0.5, 1) / k, // energy at the large scales
w: range(rng, 0.4, 1.4), // temporal frequency
phi: range(rng, 0, Math.PI * 2),
});
}
const norm = modes.reduce((s, m) => s + m.a * Math.hypot(m.kx, m.ky), 0) || 1;
const g = o.gust * o.strength / norm; // mode velocity normaliser
const dx0 = o.drift[0] * o.strength, dy0 = o.drift[1] * o.strength;
// instantaneous velocity: u = ∂ψ/∂y, v = −∂ψ/∂x (divergence-free) + drift
const vec = (x, y, t = 0) => {
let u = 0, v = 0;
for (const m of modes) {
const c = m.a * Math.cos(m.kx * x + m.ky * y + m.w * t + m.phi);
u += m.ky * c; v -= m.kx * c;
}
return { u: u * g * modes.length + dx0, v: v * g * modes.length + dy0 };
};
// Eulerian time integral 0..T of the velocity at a fixed point — the
// closed-form "memory" of the wind (dune displacement field)
const displacement = (x, y, T = 1) => {
let dx = 0, dy = 0;
for (const m of modes) {
const p = m.kx * x + m.ky * y + m.phi;
const s = m.a * (Math.sin(p + m.w * T) - Math.sin(p)) / m.w;
dx += m.ky * s; dy -= m.kx * s;
}
return { dx: dx * g * modes.length + dx0 * T, dy: dy * g * modes.length + dy0 * T };
};
// the streamfunction itself — a scalar to phase ripples with
const scalar = (x, y, t = 0) => {
let s = 0;
for (const m of modes) s += m.a * Math.sin(m.kx * x + m.ky * y + m.w * t + m.phi);
return s * o.strength / (modes.reduce((q, m) => q + m.a, 0) || 1);
};
return { vec, displacement, scalar };
}

View File

@@ -43,6 +43,13 @@ export function carpetSVG(size, opts = {}) {
blips: 1.0, // density of the spiralling wave-packet excitations
hue: 0.52, hue2: 0.55, sat: 0.55, lightNear: 0.34, lightFar: 0.62,
strokeNear: 1.7, strokeFar: 0.5,
// optional field injections (all default OFF → output unchanged):
// warpFn(t, d) → extra normalized vertical displacement (×amp) — an
// external field (e.g. wind) shaping the rows
// phaseFn(t, r) → phase shift added to every base mode (shimmer)
// modesFn(rng, o) → replace the base mode set entirely (e.g. a CMB
// power-spectrum-shaped spectrum)
warpFn: null, phaseFn: null, modesFn: null,
}, opts);
const W = size, H = size, u = size / 1000;
const paper = resolveSubstrate(o.substrate).paper.flat;
@@ -50,8 +57,10 @@ export function carpetSVG(size, opts = {}) {
// ---- base sea: low-frequency field modes (phase drifts slowly per row) ----
const M = Math.round(4 + o.chaos * 8);
const modes = [];
for (let m = 0; m < M; m++) {
let modes = [];
if (o.modesFn) {
modes = o.modesFn(rng, o);
} else for (let m = 0; m < M; m++) {
const f = range(rng, 0.4, 3.0 + o.chaos * 5); // low q: long swells dominate
modes.push({ f, a: 1 / (1 + f * 1.0), phi: range(rng, 0, Math.PI * 2), drift: range(rng, -1, 1) * (0.06 + f * 0.02) });
}
@@ -78,7 +87,8 @@ export function carpetSVG(size, opts = {}) {
const value = (t, r) => {
let s = 0;
for (const m of modes) s += m.a * Math.sin(2 * Math.PI * m.f * t + m.phi + r * m.drift);
const dphi = o.phaseFn ? o.phaseFn(t, r) : 0;
for (const m of modes) s += m.a * Math.sin(2 * Math.PI * m.f * t + m.phi + r * m.drift + dphi);
s /= norm;
let blip = 0;
for (const e of exc) {
@@ -112,7 +122,8 @@ export function carpetSVG(size, opts = {}) {
for (let i = 0; i <= npts; i++) {
const t = i / npts;
const e = env(t);
pts.push({ x: cx - half + t * 2 * half, y: baseY[r] - amp * e * value(t, r) - amp * 0.55 * (e - eMin) * d });
const warp = o.warpFn ? o.warpFn(t, d) * amp : 0;
pts.push({ x: cx - half + t * 2 * half, y: baseY[r] - amp * e * value(t, r) - amp * 0.55 * (e - eMin) * d + warp });
}
const path = smoothPath(pts);
const hue = o.hue + (o.hue2 - o.hue) * (1 - d);

View File

@@ -26,6 +26,8 @@ export const SUBSTRATES = {
vign: [42, 38, 54], feature: [58, 50, 72] },
cyanotype:{ paper: { flat: [22, 48, 82], glowIn: [38, 70, 110], glowOut: [10, 22, 44] },
vign: [6, 12, 22], feature: [180, 210, 235] },
dune: { paper: { flat: [212, 196, 158], glowIn: [230, 214, 176], glowOut: [178, 160, 122] },
vign: [58, 46, 28], feature: [120, 96, 60] },
};
export function resolveSubstrate(id = 'vintage') {

View File

@@ -48,6 +48,8 @@ export function perspFloorSVG(size, opts = {}) {
nx: 14, nz: 22, yaw: 0, pitch: 0.5, roll: 0, persp: 1.0, dist: 3.0, zShift: 0,
scale: 1.0, originX: 0, originY: 0.32,
rippleAmp: 0, rippleFreqI: 0.5, rippleFreqK: 0.4, ripplePhase: 0,
heightFn: null, // optional heightFn(i,k) → extra floor height (an external
// field: wind ripple, a helical rail, …). null → unchanged.
hue: 0.56, hue2: 0.5, sat: 0.26, lightNear: 0.32, lightFar: 0.62,
stroke: 1.5, strokeFar: 0.45, opacityMul: 1,
// high-Q RESONANCE on one axis, in a distinct colour (a standing wave threading
@@ -65,7 +67,8 @@ export function perspFloorSVG(size, opts = {}) {
// project a floor point (i, k); returns screen px + depth + clip flag
const proj = (i, k) => {
const yWorld = o.rippleAmp ? o.rippleAmp * Math.sin(o.rippleFreqI * i + o.rippleFreqK * k + o.ripplePhase) : 0;
const yWorld = (o.rippleAmp ? o.rippleAmp * Math.sin(o.rippleFreqI * i + o.rippleFreqK * k + o.ripplePhase) : 0)
+ (o.heightFn ? o.heightFn(i, k) : 0);
let x = i, y = yWorld, z = k;
const x1 = x * cy + z * sy, z1 = -x * sy + z * cy, y1 = y;
const y2 = y1 * cx - z1 * sx, z2 = y1 * sx + z1 * cx, x2 = x1;

View File

@@ -77,6 +77,7 @@ const TONES = {
cool: { flat: [180, 194, 209], gin: [199, 213, 227], gout: [146, 160, 178], vign: [30, 40, 56] },
olive: { flat: [194, 197, 160], gin: [213, 216, 178], gout: [158, 161, 122], vign: [42, 46, 26] },
neutral: { flat: [197, 196, 194], gin: [216, 215, 213], gout: [166, 165, 162], vign: [46, 45, 43] },
dune: { flat: [212, 196, 158], gin: [230, 214, 176], gout: [178, 160, 122], vign: [58, 46, 28] },
};
export function paperTone(params, inv) {
const tone = TONES[params.paperTone] || TONES.cream;
@@ -127,14 +128,14 @@ function betaColor(beta, inv) {
return hslToRgb(h, 0.68, inv ? (0.42 - 0.08 * (1 - b)) : 0.6);
}
// one distinct hue per particle kind (categorical)
const KIND_HUE = { primary: 0.08, delta: 0.50, cosmic: 0.62, sweep: 0.34, vdecay: 0.85 };
const KIND_HUE = { primary: 0.08, delta: 0.50, cosmic: 0.62, sweep: 0.34, vdecay: 0.85, pedicel: 0.26, floret: 0.93, seed: 0.12 };
function kindColor(kind, inv) {
return hslToRgb(KIND_HUE[kind] ?? 0.08, 0.72, inv ? 0.42 : 0.60);
}
// a restricted analogous scheme: each particle type a distinct shade within the
// purple→magenta→pink band (for the simplified "magenta family" feel)
// purple→magenta→pink band, nudged off the blue end (less blue in the purples)
const MAG_HUE = { primary: 0.90, delta: 0.95, cosmic: 0.82, sweep: 0.86, vdecay: 0.79 };
const MAG_HUE = { primary: 0.90, delta: 0.95, cosmic: 0.82, sweep: 0.86, vdecay: 0.79, pedicel: 0.86, floret: 0.92, seed: 0.82 };
// the contrasting feature/disk accent — a single monochrome hue. Defaults to the
// burnt-orange that pairs with the magenta family; `magentarise` exposes hue/sat as
// dials (diskHue/diskSat) so the trace-vs-disk colour relationship is sweepable.
@@ -226,6 +227,33 @@ export const PALETTES = {
feature: (e) => featureHue(e.inv, e.diskHue ?? 0.06, e.diskSat ?? 0.82), // disk + furniture
},
// the dune & lake botanical feel: pedicels keep a dry sage-stem green,
// florets glow dusty milkweed rose intensifying along the life (vertex →
// floret knob), released seeds run near-paper cream — pale silk on the wind.
// The feature ink is driftwood brown for any furniture.
dunelake: {
id: 'dunelake', label: 'Dune & lake (milkweed)',
bubbleInk: (b, e) => {
const t = Math.round(clamp(b.life, 0, 1) * 16) / 16;
const k = b.track.kind;
if (k === 'seed') {
// silk: faint at release, gathering ink toward the landing
const lo = e.inv ? 0.88 : 0.30, hi = e.inv ? 0.66 : 0.55;
return hslToRgb(0.10, 0.30, lo + (hi - lo) * t);
}
if (k === 'floret') {
// dusty rose, saturating into the rosette tip
const lo = e.inv ? 0.74 : 0.34, hi = e.inv ? 0.52 : 0.58;
return hslToRgb(0.93 + (e.traceHue ?? 0), 0.38 + 0.30 * t, lo + (hi - lo) * t);
}
// pedicel (and any guest kind): sage stem, faint at the vertex,
// warming toward the floret end
const lo = e.inv ? 0.80 : 0.22, hi = e.inv ? 0.50 : 0.46;
return hslToRgb(0.24 - 0.10 * t, 0.30 + 0.18 * t, lo + (hi - lo) * t);
},
feature: (e) => hslToRgb(0.08, 0.45, e.inv ? 0.40 : 0.34), // driftwood
},
// a complete "chemistry": overrides paper + ink together (deep Prussian-blue
// ground, pale lines) — the blueprint/cyanotype look.
cyanotype: {

View File

@@ -22,6 +22,8 @@ const TRACK_LAYERS = [
{ id: 'tracks-cosmic', label: 'Tracks · cosmic & sweepers', kinds: ['cosmic', 'sweep'] },
{ id: 'tracks-vdecay', label: 'Tracks · V-decays', kinds: ['vdecay'] },
{ id: 'tracks-delta', label: 'Tracks · δ-rays (curls)', kinds: ['delta'] },
{ id: 'tracks-flora', label: 'Flora · umbels & florets', kinds: ['pedicel', 'floret'] },
{ id: 'tracks-drift', label: 'Drift · seeds on the wind', kinds: ['seed'] },
];
// rgb[0..255] → [h,s,l] (0..1), for hue-preserving darkening

73
src/scene/drift.js Normal file
View File

@@ -0,0 +1,73 @@
/* ============================================================
drift.js — milkweed seeds as test particles in the wind.
The sibling of track.js: where a charged particle is BENT by
the B-field, a coma-tufted seed is ADVECTED by the flow. Same
output contract ({pts:[{x,y,beta,theta}], kind, …}), so the
shared bubble sampler and both renderers work verbatim.
β ↔ airspeed is the energy-loss analogue: a stalling seed =
rising ionisation = denser bubbles, so landings end in the
same dense terminal cluster as a particle's end of range.
============================================================ */
import { gauss, range } from '../rng.js';
/* Advect one seed through the wind. state: {x, y, theta, slip}
(slip = the seed's own airspeed along its heading, decaying). */
export function advectTrack(state, wind, opts = {}) {
const o = Object.assign({
maxSteps: 1400, ds: 0.004, bound: 1.15,
settle: 0.06, // |v| below this → the seed lands
flutter: 0.5, // coma wobble (perpendicular jitter)
drag: 2.2, // slip decay per unit path
vRef: 1.0, // speed that maps to β ≈ 0.9
rng: Math.random,
}, opts);
let { x, y, theta, slip = 0.3 } = state;
const pts = [];
for (let i = 0; i < o.maxSteps; i++) {
const w = wind.vec(x, y);
let vx = w.u + Math.cos(theta) * slip;
let vy = w.v + Math.sin(theta) * slip;
const sp = Math.hypot(vx, vy);
if (sp < o.settle && i > 4) break; // stalled → landed
const beta = Math.min(0.92, Math.max(0.15, (sp / o.vRef) * 0.9));
pts.push({ x, y, beta, theta: Math.atan2(vy, vx) });
x += (vx / sp) * o.ds; y += (vy / sp) * o.ds;
// coma flutter: a small heading kick, stronger when the air is slow
theta = Math.atan2(vy, vx) + gauss(o.rng) * o.flutter * 0.09 / Math.max(sp, 0.35);
slip *= Math.max(0, 1 - o.drag * o.ds);
if (Math.abs(x) > o.bound || Math.abs(y) > o.bound) break;
}
return pts;
}
/* Wrap released seeds into renderable tracks. Each seed may carry a small
"tuft bundle" of offset sub-tracks — the parallel silks of the coma. */
export function generateDrift(params, wind, seeds, rng) {
const o = Object.assign({
count: 24, // cap on seeds taken
slip: 0.35, flutter: 0.5, settle: 0.06, vRef: 1.0,
tuft: 2, // sub-tracks per seed beyond the main silk
}, params);
const tracks = [];
const take = seeds.slice(0, Math.max(0, Math.round(o.count)));
for (const s of take) {
const n = 1 + Math.max(0, Math.round(o.tuft));
for (let k = 0; k < n; k++) {
const perp = s.theta + Math.PI / 2;
const off = k === 0 ? 0 : gauss(rng) * 0.005;
const pts = advectTrack(
{ x: s.x + Math.cos(perp) * off, y: s.y + Math.sin(perp) * off, theta: s.theta, slip: o.slip * range(rng, 0.7, 1.3) },
wind,
{ flutter: o.flutter, settle: o.settle, vRef: o.vRef, rng },
);
if (pts.length < 2) continue;
tracks.push({
pts, kind: 'seed', weight: k === 0 ? range(rng, 0.65, 0.85) : range(rng, 0.35, 0.55),
q: 1, z: gauss(rng) * 0.3, age: 0,
jitter: 0.004, densityScale: 1.4, sizeScale: 0.8,
});
}
}
return tracks;
}

80
src/scene/umbel.js Normal file
View File

@@ -0,0 +1,80 @@
/* ============================================================
umbel.js — the milkweed flower head as a particle vertex.
An umbel IS interaction-vertex geometry grown botanically:
3080 pedicels radiate from one point, each terminating in a
five-fold floret. We model pedicels as direct parametric arcs
(the delta.js precedent — shape the curve, don't fight the
integrator). β runs HIGH at the vertex and falls toward the
tip, so 1/β² bubble density concentrates ink at the floret
knob — the head reads as a vertex with glowing terminals.
burst (0..1) releases pedicels: the floret is gone to silk,
the pedicel frays, and a seed {x, y, theta} is handed to the
drift system. Release decisions come from the SAME rng stream,
so flora and drift layers calling generateUmbel with identical
inputs agree exactly on the release points (no shared state).
============================================================ */
import { gauss, range } from '../rng.js';
/* Build one umbel. Returns { tracks, seeds }. */
export function generateUmbel(params, rng) {
const o = Object.assign({
x: 0, y: 0, scale: 0.5, // scale ~ head radius in frame units × 0.36
pedicels: 48, droop: 0.35, // gravity sag of each pedicel
spread: 1.0, // angular jitter / fan looseness
burst: 0, // 0 closed … 1 fully released to seed
floretSize: 1,
}, params);
const tracks = [], seeds = [];
const R = 0.18 * o.scale; // pedicel reach
const N = Math.max(3, Math.round(o.pedicels));
for (let i = 0; i < N; i++) {
const ang = (i / N) * Math.PI * 2 + gauss(rng) * 0.22 * o.spread;
const len = R * range(rng, 0.82, 1.12);
const droopAmt = o.droop * (0.5 + 0.5 * rng()) * len;
const released = rng() < o.burst;
const reach = released ? range(rng, 0.65, 0.9) : 1; // frayed pedicels run short
const steps = 22;
const pts = [];
for (let s = 0; s <= steps; s++) {
const f = (s / steps) * reach;
// radial run + quadratic gravity sag (the umbel's soft droop)
const px = o.x + Math.cos(ang) * len * f;
const py = o.y + Math.sin(ang) * len * f + droopAmt * f * f;
// β: hot at the vertex → slow at the tip ⇒ ink gathers at the floret
const beta = 0.8 - 0.6 * f;
const theta = Math.atan2(Math.sin(ang) * len + 2 * droopAmt * f, Math.cos(ang) * len);
pts.push({ x: px, y: py, beta, theta });
}
tracks.push({
pts, kind: 'pedicel', weight: range(rng, 0.8, 1), q: i % 2 ? 1 : -1,
z: gauss(rng) * 0.2, age: 0, jitter: released ? 0.0034 : 0.0016,
});
const tip = pts[pts.length - 1];
if (released) {
seeds.push({ x: tip.x, y: tip.y, theta: tip.theta });
continue; // gone to silk — no floret
}
// five-fold floret: petal strokes fanned about the tip tangent
const fl = 0.016 * o.scale * o.floretSize;
const off = range(rng, 0, Math.PI * 2);
for (let p = 0; p < 5; p++) {
const pa = off + (p / 5) * Math.PI * 2;
const fpts = [];
for (let s = 0; s <= 6; s++) {
const f = s / 6;
fpts.push({
x: tip.x + Math.cos(pa) * fl * f, y: tip.y + Math.sin(pa) * fl * f,
beta: 0.25, theta: pa,
});
}
tracks.push({
pts: fpts, kind: 'floret', weight: range(rng, 0.7, 0.95), q: 1,
z: gauss(rng) * 0.15, age: 0, sizeScale: 1.2,
});
}
}
return { tracks, seeds };
}

124
tools/dunelake.mjs Normal file
View File

@@ -0,0 +1,124 @@
/* ============================================================
dunelake.mjs — DUNES ON A GREAT LAKE WITH MILKWEED.
First plate of the hidden-realities series: one invisible wind
field made visible by three detectors — dunes (its time
integral, what the sand remembers), water (its instantaneous
trace), milkweed seeds (its advection). The umbel is a particle
vertex grown botanically.
A curated variation matrix in the liberation.mjs manner:
worlds (seed × wind character) × horizons × milkweed states ×
palettes. Emits SVGs + exportable .mjs compositions + gallery.
Usage: node tools/dunelake.mjs [size]
============================================================ */
import { writeFileSync, mkdirSync } from 'node:fs';
import { DEFAULT_COMPOSITION } from '../src/compose/schema.js';
import { renderComposition } from '../src/compose/composition.js';
const SIZE = +(process.argv[2] || 1300);
const OUT = 'output/dunelake';
mkdirSync(OUT, { recursive: true });
const merge = (a, b) => { const o = { ...a }; for (const k in b) o[k] = (b[k] && typeof b[k] === 'object' && !Array.isArray(b[k])) ? merge(a[k] || {}, b[k]) : b[k]; return o; };
/* The landscape base: no collision event, no sun — the subjects are the
field's three detectors. horizon = waterline (frame fraction). */
const base = (seed, windSeed, windStrength, horizon, over = {}) => {
const hClip = horizon; // lake band ends where dunes begin
const c = merge(JSON.parse(JSON.stringify(DEFAULT_COMPOSITION)), merge({
seed,
wind: { seed: windSeed, strength: windStrength, scale: 1.6, gust: 0.4, time: 1.4 },
background: {
color: 'rgb(226,216,192)',
glow: { strength: 0.4, followSun: false },
vignette: { strength: 0.32, mode: 'radial', cy: 0.42, radius: 0.9 },
film: { opacity: 0.5 }, aging: { opacity: 0.35, scratches: 3 }, grain: { opacity: 0.38 },
},
// WATER: a flat far band, instantaneous wind, cool — clipped above the dune line
fieldLake: {
enabled: true, seed: 'LIMNOS-' + seed,
color: { hueBack: 0.555, hueFront: 0.52, sat: 0.42, light: 0.52 },
layers: 2, chaos: 0.1, blips: 0.2, mound: 0, lines: 24,
horizonY: Math.max(0.2, horizon - 0.16), wind: { warp: 0.55 },
clip: [0, Math.max(0, horizon - 0.2), 1, horizon + 0.02],
},
// DUNES: the ridgeline carpet warped by the wind's time integral, warm sand
fieldSea: {
seed: 'AEOLIAN-' + seed,
color: { hueBack: 0.09, hueFront: 0.07, sat: 0.42, light: 0.42 },
layers: 3, chaos: 0.22, blips: 0.35, mound: 0.55, horizonY: horizon, lines: 52,
wind: { warp: 0.9 },
},
fieldGrid: { enabled: false },
disk: { enabled: false },
bubble: { enabled: false },
// MILKWEED: vertex-flowers on the foredune
flora: {
enabled: true, seed: 'ASCLEPIAS-' + seed, palette: 'dunelake',
count: 3, scale: 0.42, pedicels: 46, droop: 0.4, burst: 0.25,
clusterW: 0.7, clusterH: 0.3,
transform: { x: -0.34, y: 0.42, rotation: 0, scale: 1 },
},
drift: { enabled: true, count: 28, slip: 0.4, flutter: 0.55, tuft: 2 },
fiduciaries: { enabled: true, label: 'ASCLEPIAS SYRIACA', arrow: true, target: [-0.34, 0.42], from: [0.35, -0.3], caption: 'wind · integrated | instantaneous | advected' },
}, over));
return c;
};
// dusk: warm-on-cool — sand holds the late light, the water goes slate
const dusk = {
background: { color: 'rgb(210,190,170)', glow: { strength: 0.5 } },
fieldSea: { color: { hueBack: 0.05, hueFront: 0.95, sat: 0.5, light: 0.4 } },
fieldLake: { color: { hueBack: 0.62, hueFront: 0.58, sat: 0.5, light: 0.42 } },
};
// nocturne: the night-plate move — dark ground, pale silks (liberation #11)
const nocturne = {
background: { color: 'rgb(22,23,30)', glow: { strength: 0.3, followSun: false }, vignette: { strength: 0.2 }, film: { opacity: 0.28 }, grain: { opacity: 0.3 } },
fieldSea: { color: { hueBack: 0.6, hueFront: 0.08, sat: 0.3, light: 0.62 } },
fieldLake: { color: { hueBack: 0.58, hueFront: 0.56, sat: 0.4, light: 0.66 } },
fiduciaries: { pencil: '#cfc4a8' },
};
const W = [
{ id: 'breeze', seed: 'ZEPHYR-011', strength: 0.55 },
{ id: 'gale', seed: 'BOREAS-104', strength: 1.25 },
];
const SEEDS = ['LAKEMICH-0610', 'SLEEPING-BEAR-22', 'CRITICALDUNE-7'];
const V = [];
for (const s of SEEDS) {
for (const w of W) {
const tag = `${s.split('-')[0].toLowerCase()}-${w.id}`;
// big-sky vs big-water horizons
V.push({ name: `${tag}_low-horizon`, note: `${w.id} · big sky, dunes low (${s})`,
comp: base(s, w.seed, w.strength, 0.62) });
V.push({ name: `${tag}_high-water`, note: `${w.id} · big water, high dune line (${s})`,
comp: base(s, w.seed, w.strength, 0.44, { fieldSea: { lines: 44 }, flora: { transform: { x: -0.3, y: 0.5 } }, fiduciaries: { target: [-0.3, 0.5] } }) });
}
}
// milkweed states — closed / bursting / full release streaming across the lake
const sBurst = SEEDS[0], sFull = SEEDS[1];
V.push({ name: 'states_closed-left', note: 'umbels closed — the vertex before the decay',
comp: base(sBurst, W[0].seed, W[0].strength, 0.56, { flora: { burst: 0, count: 2, transform: { x: -0.5, y: 0.38 } }, drift: { enabled: false }, fiduciaries: { target: [-0.5, 0.38], caption: 'pre-release · all florets held' } }) });
V.push({ name: 'states_bursting-right', note: 'right-third bursting — half the florets gone to silk',
comp: base(sBurst, W[1].seed, W[1].strength, 0.56, { flora: { burst: 0.55, transform: { x: 0.42, y: 0.44 } }, drift: { count: 40 }, fiduciaries: { target: [0.42, 0.44], caption: 'release fraction 0.55' } }) });
V.push({ name: 'states_full-release', note: 'off-corner full burst — seeds streaming across the water',
comp: base(sFull, W[1].seed, 1.4, 0.5, { flora: { burst: 1, count: 2, transform: { x: -0.78, y: 0.62 }, scale: 0.5 }, drift: { count: 64, slip: 0.5 }, fiduciaries: { target: [-0.78, 0.62], from: [0.4, -0.35], caption: 'all silk · the field made visible' } }) });
// palette rows over one world: day-sand (the base), dusk, nocturne
V.push({ name: 'palette_dusk', note: 'dusk — sand holds the late light, slate water',
comp: base(SEEDS[2], W[0].seed, 0.8, 0.55, dusk) });
V.push({ name: 'palette_nocturne', note: 'nocturne — dark plate, pale silks (night-plate move)',
comp: base(SEEDS[2], W[1].seed, 1.1, 0.55, merge(nocturne, { flora: { burst: 0.6 }, drift: { count: 48 } })) });
console.log(`dunelake — ${V.length} compositions → ${OUT}/`);
for (const v of V) {
writeFileSync(`${OUT}/${v.name}.svg`, renderComposition(v.comp, SIZE));
writeFileSync(`${OUT}/${v.name}.mjs`, `/* dune-lake-milkweed · ${v.note} */\nexport const composition = ${JSON.stringify(v.comp, null, 2)};\n`);
console.log(` ${v.name}${v.note}`);
}
writeFileSync(`${OUT}/matrix.html`, `<!DOCTYPE html><html><head><meta charset="utf-8"><title>Dunes on a Great Lake — one wind, three detectors</title>
<style>html,body{margin:0;background:#15140f;color:#cabfa6;font:12px ui-monospace,monospace}h1{font-weight:400;letter-spacing:.2em;text-transform:uppercase;color:#9fb7af;padding:18px 14px 0}p.sub{color:#8a8068;padding:0 14px;margin:6px 0 0}.grid{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;padding:14px}figure{margin:0;position:relative;background:#fff;overflow:hidden}img{width:100%;display:block}figcaption{position:absolute;left:0;bottom:0;right:0;padding:6px 9px;font:12px ui-monospace,monospace;color:#fff;background:linear-gradient(transparent,#000d)}</style></head><body>
<h1>Dunes on a Great Lake with Milkweed (${V.length})</h1>
<p class="sub">one invisible wind field · three detectors — sand (integrated) · water (instantaneous) · seeds (advected)</p>
<div class="grid">${V.map(v => `<figure><img src="${v.name}.svg"><figcaption>${v.note}</figcaption></figure>`).join('')}</div></body></html>`);
console.log(`-> ${OUT}/matrix.html`);