Setting the file. One moment.
Defaults · Frost Sequence Camera Orbit · heygen-com/hyperframes · Skills Docs
ContentsBack to the top of the page Three Mesh BVH LICENSE
source/src/dials/defaults.ts
source/src/dials/ defaults.ts
TypeScript · 520 lines · 21 KB
as
unknown
as
string
[],
7 default: def,
8 });
9 /** `?lite=1` lowers every heavy default (used for headless technical checks on software WebGPU). */
10 export const LITE =
11 typeof location !== "undefined" && new URLSearchParams (location.search). has ( "lite" );
12
13 export const presetsDefaults = {
14 copyAllJson: { type: "action" as const , label: "Copy all params as JSON" },
15 } satisfies DialConfig ;
16
17 export const SHAPES = [ "torus" , "sphere" , "roundedBox" , "pyramid" , "icosahedron" , "logo" ] as const ;
18 export type ShapeName = ( typeof SHAPES )[ number ];
19
20 export const shapeDefaults = {
21 shape: sel ( "torus" , SHAPES ),
22 size: [ 1.0 , 0.5 , 1.6 , 0.01 ],
23 tubeRatio: [ 0.45 , 0.2 , 0.7 , 0.005 ],
24 deformAmplitude: [ 0.045 , 0 , 0.2 , 0.001 ],
25 deformFrequency: [ 1.1 , 0.3 , 4 , 0.05 ],
26 segments: [ 256 , 64 , 512 , 8 ],
27 seed: [ 7 , 0 , 999 , 1 ],
28 // world-space offset of the object (hero pages override this from their own panel)
29 offsetX: [ 0 , - 5 , 5 , 0.01 ],
30 offsetY: [ 0 , - 4 , 4 , 0.01 ],
31 offsetZ: [ 0 , - 4 , 4 , 0.01 ],
32 // extrusion of /public/logo.svg when shape = logo (fractions are relative to `logo.width`)
33 logo: {
34 _collapsed: true ,
35 width: [ 2.6 , 0.8 , 5 , 0.05 ],
36 depth: [ 0.22 , 0.02 , 1 , 0.005 ],
37 bevelThickness: [ 0.06 , 0 , 0.3 , 0.002 ],
38 bevelSize: [ 0.05 , 0 , 0.3 , 0.002 ],
39 bevelOffset: [ 0 , - 0.1 , 0.1 , 0.002 ],
40 bevelSegments: [ 5 , 1 , 16 , 1 ],
41 cornerRadius: [ 0.03 , 0 , 0.15 , 0.001 ],
42 curveSegments: [ 24 , 2 , 64 , 1 ],
43 creaseAngle: [ 40 , 0 , 180 , 1 ],
44 sdfRes: [ 64 , 32 , 128 , 8 ],
45 },
46 } satisfies DialConfig ;
47
48 export const iceDefaults = {
49 ior: [ 1.36 , 1.0 , 2.0 , 0.005 ],
50 dispersion: [ 0 , 0 , 0.3 , 0.005 ],
51 thicknessScale: [ 1.0 , 0.1 , 3 , 0.01 ],
52 attenuationDistance: [ 0.9 , 0.05 , 5 , 0.01 ],
53 attenuationColor: "#cfd2d4" ,
54 baseRoughness: [ 0.05 , 0 , 0.5 , 0.005 ],
55 frost: {
56 scale: [ 1.6 , 0.2 , 6 , 0.05 ],
57 threshold: [ 0.5 , 0 , 1 , 0.01 ],
58 softness: [ 0.28 , 0.01 , 1 , 0.01 ],
59 roughness: [ 0.65 , 0 , 1 , 0.01 ],
60 diffuse: [ 0.55 , 0 , 1 , 0.01 ],
61 crystalBump: [ 0.12 , 0 , 1 , 0.01 ],
62 crystalScale: [ 28 , 4 , 80 , 1 ],
63 },
64 cracks: {
65 largeScale: [ 1.6 , 0.3 , 8 , 0.05 ],
66 warp: [ 0.45 , 0 , 1.5 , 0.01 ],
67 warpScale: [ 1.4 , 0.2 , 6 , 0.05 ],
68 coverage: [ 0.55 , 0 , 1 , 0.01 ],
69 regionScale: [ 0.8 , 0.1 , 4 , 0.05 ],
70 regionCoverage: [ 0.7 , 0 , 1 , 0.01 ],
71 veinScale: [ 5 , 1 , 20 , 0.25 ],
72 veinContrast: [ 0.7 , 0 , 1 , 0.01 ],
73 width: [ 0.004 , 0.0005 , 0.02 , 0.0005 ],
74 brightness: [ 0.4 , 0 , 3 , 0.01 ],
75 darkness: [ 0.5 , 0 , 1 , 0.01 ],
76 refraction: [ 0.02 , 0 , 0.1 , 0.001 ],
77 surfaceStrength: [ 0.35 , 0 , 1 , 0.01 ],
78 steps: [ 14 , 4 , 32 , 1 ],
79 fineCracks: true ,
80 fineScale: [ 5.5 , 2 , 20 , 0.1 ],
81 fineAmount: [ 0.45 , 0 , 1 , 0.01 ],
82 fineCoverage: [ 0.35 , 0 , 1 , 0.01 ],
83 fineNearLarge: [ 0.75 , 0 , 1 , 0.01 ],
84 },
85 smudges: {
86 amount: [ 0.6 , 0 , 1 , 0.01 ],
87 coverage: [ 0.55 , 0 , 1 , 0.01 ],
88 maskScale: [ 1.2 , 0.1 , 6 , 0.05 ],
89 anisotropy: [ 6 , 1 , 20 , 0.5 ],
90 roughness: [ 0.35 , 0 , 1 , 0.01 ],
91 whiteness: [ 0.08 , 0 , 0.5 , 0.005 ],
92 scale: [ 3.2 , 0.5 , 10 , 0.1 ],
93 },
94 bumps: {
95 microBump: [ 0.25 , 0 , 1 , 0.005 ],
96 microScale: [ 60 , 10 , 200 , 1 ],
97 microCoverage: [ 0.6 , 0 , 1 , 0.01 ],
98 rippleBump: [ 0.3 , 0 , 1 , 0.01 ],
99 rippleScale: [ 9 , 2 , 30 , 0.5 ],
100 maskScale: [ 1.5 , 0.1 , 6 , 0.05 ],
101 },
102 // emissive glow of partially eroded (crumbling) ice along the view ray; the old value was 1 and blew out
103 crumbleGlow: [ 0.15 , 0 , 2 , 0.01 ],
104 // how much the crumbly edge band around a cut whitens and opacifies the ice (0 = stays glassy)
105 edgeWhiteness: [ 0.5 , 0 , 1 , 0.01 ],
106 clearcoat: [ 0.3 , 0 , 1 , 0.01 ],
107 clearcoatRoughness: [ 0.08 , 0 , 1 , 0.005 ],
108 envIntensity: [ 0.55 , 0 , 3 , 0.01 ],
109 specularIntensity: [ 1.0 , 0 , 2 , 0.01 ],
110 interiorScatter: [ 0.25 , 0 , 1 , 0.01 ],
111 } satisfies DialConfig ;
112
113 export const erosionDefaults = {
114 resolution: sel ( "128" , [ "64" , "96" , "128" , "192" ] as const ),
115 brushRadius: [ 0.28 , 0.05 , 0.8 , 0.005 ],
116 brushSoftness: [ 0.5 , 0 , 1 , 0.01 ],
117 brushStrength: [ 2.2 , 0.1 , 8 , 0.05 ],
118 brushSpeedRef: [ 3.0 , 0.2 , 12 , 0.1 ],
119 brushNoise: [ 0.55 , 0 , 1 , 0.01 ],
120 brushNoiseScale: [ 9 , 2 , 30 , 0.5 ],
121 throughDepth: [ 1.0 , 0 , 1 , 0.01 ],
122 // ice breaks along cells: the brush is evaluated per break cell (flat facets) and mixed with the smooth capsule
123 cellSnap: [ 0.85 , 0 , 1 , 0.01 ],
124 breakCellScale: [ 3.0 , 0.5 , 8 , 0.1 ],
125 crumbleRate: [ 1.6 , 0 , 6 , 0.05 ],
126 crumbleCrackBias: [ 2.5 , 0 , 6 , 0.05 ],
127 crumbleDuration: [ 0.5 , 0 , 2 , 0.01 ],
128 // the surface is gone where erosion exceeds cutThreshold (narrow stochastic transition, resolved by TRAA);
129 // the crumbly edge band sits just below it, edgeWidth wide
130 cutThreshold: [ 0.7 , 0.3 , 0.98 , 0.01 ],
131 cutSoftness: [ 0.04 , 0.005 , 0.3 , 0.005 ],
132 edgeWidth: [ 0.35 , 0.05 , 0.8 , 0.01 ],
133 // the cut surface seen through a hole: ray-march steps through the field, and extra frost on the fresh break
134 interiorSteps: [ 24 , 8 , 64 , 1 ],
135 interiorFrost: [ 0 , 0 , 1 , 0.01 ],
136 edgeInset: [ 0.06 , 0 , 0.3 , 0.005 ],
137 edgeBump: [ 0.6 , 0 , 2 , 0.01 ],
138 edgeBumpScale: [ 55 , 10 , 160 , 1 ],
139 } satisfies DialConfig ;
140
141 export const powderDefaults = {
142 particleCount: sel ( "1M" , [ "100k" , "250k" , "500k" , "1M" , "2M" ] as const ),
143 // fraction of the eroded volume that becomes visible powder (the rest simply vanishes)
144 amount: [ 0.3 , 0.02 , 1 , 0.01 ],
145 grainSizeMultiplier: [ 1.0 , 0.2 , 4 , 0.05 ],
146 // where the interior sample points sit: fraction placed within `nearSurfaceDepth` of the surface
147 nearSurfaceFraction: [ 0.6 , 0 , 1 , 0.01 ],
148 nearSurfaceDepth: [ 0.2 , 0.05 , 0.5 , 0.01 ],
149 grainSizes: {
150 // four classes, tiny -> large; the ratios are normalised, the leftover ~1% is large fragments
151 tinyDustRatio: [ 0.7 , 0 , 1 , 0.01 ],
152 smallGrainRatio: [ 0.22 , 0 , 1 , 0.01 ],
153 mediumClumpRatio: [ 0.07 , 0 , 1 , 0.01 ],
154 tinyDustSize: [ 0.0055 , 0.001 , 0.03 , 0.0005 ],
155 smallGrainSize: [ 0.011 , 0.002 , 0.05 , 0.0005 ],
156 mediumClumpSize: [ 0.022 , 0.005 , 0.1 , 0.001 ],
157 largeFragmentSize: [ 0.05 , 0.01 , 0.2 , 0.001 ],
158 sizeJitter: [ 0.45 , 0 , 1 , 0.01 ],
159 },
160 grainVariants: [ 5 , 1 , 6 , 1 ],
161 // flat-shaded low-poly splinters instead of smooth blobs (each face catches the key light separately)
162 facetedGrains: false ,
163 // grains never render below this many pixels (sub-pixel dust flickers under TRAA)
164 minPixelSize: [ 1.5 , 0 , 4 , 0.05 ],
165 ejectSpeed: [ 1.1 , 0 , 4 , 0.01 ],
166 // grains released after the cursor stopped (crumble) still get at least this kick, units/s
167 minEjectSpeed: [ 1.0 , 0 , 4 , 0.01 ],
168 ejectSpread: [ 0.5 , 0 , 3 , 0.01 ],
169 ejectTurbulence: [ 0.8 , 0 , 4 , 0.01 ],
170 backwardRatio: [ 0.25 , 0 , 1 , 0.01 ],
171 clumpSpeedJitter: [ 0.5 , 0 , 1 , 0.01 ],
172 drag: [ 1.4 , 0 , 8 , 0.01 ],
173 gravity: [ 0.12 , 0 , 2 , 0.005 ],
174 turbulence: [ 0.9 , 0 , 4 , 0.01 ],
175 turbulenceScale: [ 1.8 , 0.2 , 8 , 0.05 ],
176 turbulenceDecay: [ 0.6 , 0.05 , 4 , 0.01 ],
177 clumpCohesion: [ 2.4 , 0 , 10 , 0.05 ],
178 clumpCount: [ 24000 , 500 , 100000 , 500 ],
179 settleTime: [ 3.2 , 0.3 , 12 , 0.05 ],
180 settledDrift: [ 0.012 , 0 , 0.1 , 0.001 ],
181 tumble: [ 2.5 , 0 , 12 , 0.05 ],
182 baseTone: "#dcdcdc" ,
183 wrap: [ 0.45 , 0 , 1 , 0.01 ],
184 // ice-shard look of the grains: the backdrop shows through their centres, rims stay bright (Fresnel),
185 // real specular / clearcoat from the lights, and per-grain sparkle
186 fragments: {
187 translucency: [ 0.6 , 0 , 1 , 0.01 ],
188 throughTint: "#aeb6bb" ,
189 fresnelPower: [ 3 , 0.5 , 8 , 0.1 ],
190 roughness: [ 0.32 , 0 , 1 , 0.01 ],
191 clearcoat: [ 0.6 , 0 , 1 , 0.01 ],
192 specular: [ 1.0 , 0 , 3 , 0.01 ],
193 sparkle: [ 0.6 , 0 , 3 , 0.01 ],
194 sparkleFraction: [ 0.35 , 0 , 1 , 0.01 ],
195 sparkleSpread: [ 0.5 , 0 , 1.5 , 0.01 ],
196 },
197 // repulsion from the object: an acceleration away from the surface (full strength inside, smooth
198 // falloff over repelRange outside), added to the other forces so grains drift out organically
199 // shard sprites: every grain is a camera-facing quad textured from the shard atlas (16 photographed-looking
200 // ice shards: silhouette, bevel normal map, frost structure, thickness). Off = the low-poly mesh grains.
201 sprites: {
202 enabled: true ,
203 sizeScale: [ 1.4 , 0.3 , 4 , 0.05 ],
204 // random tilt off the camera plane (degrees): 0 = flat billboards, more = thinner, more varied shards
205 tilt: [ 30 , 0 , 70 , 1 ],
206 normalStrength: [ 1 , 0 , 2.5 , 0.05 ],
207 // how much the atlas' frost structure drives the look (0 = every shard clear glass)
208 frostFromAtlas: [ 1 , 0 , 1 , 0.01 ],
209 frostBoost: [ 0.35 , 0 , 1.5 , 0.01 ],
210 frostRoughness: [ 0.7 , 0 , 1 , 0.01 ],
211 // key light caught by the thin bevelled rim
212 edgeLight: [ 0.6 , 0 , 3 , 0.01 ],
213 alphaCut: [ 0.2 , 0.05 , 0.6 , 0.01 ],
214 // real transparency: how much the clear parts of a shard show what is behind them (rebuilds the powder)
215 seeThrough: [ 0 , 0 , 1 , 0.01 ],
216 },
217 repelFromObject: false ,
218 repelStrength: [ 6 , 0 , 30 , 0.1 ],
219 repelRange: [ 0.35 , 0.02 , 2 , 0.01 ],
220 // plus a push straight away from the object's centre, so grains never settle in concave pockets of
221 // the shape (the surface-normal push alone balances out inside a pocket); range in object radii
222 repelRadial: [ 12 , 0 , 60 , 0.5 ],
223 repelRadialRange: [ 1.6 , 1 , 4 , 0.05 ],
224 // hard cap on grain speed (units/s): repulsion and ejection can never fling a grain off-screen
225 maxSpeed: [ 8 , 1 , 40 , 0.1 ],
226 // a grain farther than this from the object (world units, far off screen) is brought back to its rest
227 lostRadius: [ 30 , 5 , 100 , 1 ],
228 densityShadowStrength: [ 0.75 , 0 , 2 , 0.01 ],
229 densityAOStrength: [ 0.55 , 0 , 2 , 0.01 ],
230 densityGrid: sel ( "64" , [ "32" , "48" , "64" ] as const ),
231 densityExtent: [ 4.2 , 2 , 10 , 0.1 ],
232 hazeIntensity: [ 0.22 , 0 , 1.5 , 0.005 ],
233 hazeSteps: [ 28 , 8 , 64 , 1 ],
234 inheritRotation: true ,
235 inheritTime: [ 220 , 0 , 1500 , 10 ],
236 strayCount: [ 40 , 0 , 200 , 1 ],
237 straySpeed: [ 0.06 , 0 , 0.5 , 0.005 ],
238 fragmentShadowMap: true ,
239 } satisfies DialConfig ;
240
241 export const healingDefaults = {
242 healDelay: [ 3.5 , 0 , 12 , 0.1 ],
243 // a grain also turns back on its own this long after it left (0 = only when the cursor is idle),
244 // so pieces keep cycling while you keep breaking
245 returnAfter: [ 3.0 , 0 , 20 , 0.1 ],
246 // grains fly home in a wave: the ones closest to the object first, the far plume last, spread over
247 // waveTime (a grain waveReach units away waits the full waveTime)
248 waveTime: [ 2.0 , 0 , 8 , 0.05 ],
249 waveReach: [ 3.0 , 0.2 , 10 , 0.1 ],
250 waveJitter: [ 0.6 , 0 , 3 , 0.05 ],
251 // return motion: 'spring' = a damped spring toward the rest that ramps in while the grain is still
252 // flying out (continuous decelerate / turn / accelerate back); 'path' = the eased curved path
253 returnMode: sel ( "spring" , [ "spring" , "path" ] as const ),
254 returnSpring: [ 6 , 0.5 , 40 , 0.1 ],
255 returnDamping: [ 0.9 , 0.2 , 2 , 0.01 ],
256 returnRamp: [ 0.6 , 0 , 3 , 0.05 ],
257 // share of the powder drag that still acts during the spring return (absorbs ballistic speed)
258 returnDrag: [ 0.25 , 0 , 1 , 0.01 ],
259 // speed cap on the way home (world units / s): keeps far grains visible instead of crossing the screen in a frame
260 returnMaxSpeed: [ 12 , 1 , 60 , 0.5 ],
261 landRadius: [ 0.03 , 0.005 , 0.2 , 0.005 ],
262 returnDuration: [ 2.4 , 0.2 , 8 , 0.05 ],
263 returnCurve: [ 0.35 , 0 , 1.5 , 0.01 ],
264 // a landing grain restores its whole break cell (the facet it broke out of) at this rate (1/s; 30 = instant)
265 cellRestore: [ 30 , 0 , 60 , 0.5 ],
266 // a break cell refills only once all its grains are home; allow this many still in flight
267 cellStragglers: [ 0 , 0 , 20 , 1 ],
268 // the refilling cell grows back from what is already solid (hole floor, walls, landed shards) at `cellRestore`,
269 // one voxel layer at a time, instead of the whole facet popping in
270 growFromEdges: true ,
271 // per-voxel timing variation of every refill, cleanup and refrost timer (0 = a break cell changes look all
272 // at once, 1 = it dissolves in small patches); breakupScale sets the patch size (higher = smaller)
273 breakup: [ 0.7 , 0 , 1 , 0.01 ],
274 breakupScale: [ 10 , 1 , 40 , 0.5 ],
275 // returning shards turn to lie flat on the surface they land on (0 = keep facing the camera)
276 alignToSurface: [ 1 , 0 , 1 , 0.01 ],
277 // distance from home over which a shard turns (world units)
278 alignDistance: [ 0.6 , 0.05 , 3 , 0.01 ],
279 // FROST: shape of the alignment over the flight home (1 = linear, < 1 early, > 1 late)
280 alignCurve: [ 1 , 0.2 , 4 , 0.05 ],
281 // a landed shard lies on the surface until its voxel is solid again, then fades over this many seconds
282 landedFade: [ 0.25 , 0 , 2 , 0.01 ],
283 // shrink a shard over the last part of its flight (0 = it arrives at full size)
284 landShrink: [ 0 , 0 , 0.3 , 0.005 ],
285 // plus a small sphere of voxels around its rest position (in voxels); 0 = cells only
286 depositRadius: [ 0.5 , 0 , 3 , 0.25 ],
287 // optional: hidden material comes back as extra dust materialising this far from the surface (0 = off;
288 // it never rebuilds anything by itself, only the grains that flew out do)
289 ghostReturnDistance: [ 0 , 0 , 1.5 , 0.01 ],
290 ghostFlightFraction: [ 0.45 , 0.1 , 1 , 0.01 ],
291 // voxels without a grain follow their healed neighbours at this rate, staying `healGap` behind
292 healRate: [ 0.6 , 0.02 , 3 , 0.01 ],
293 healGap: [ 0.04 , 0 , 0.5 , 0.01 ],
294 // cleanup rate for voxels no grain came back to; starts only after the wave has fully landed
295 fallbackHeal: [ 0.15 , 0 , 0.5 , 0.005 ],
296 refrostTime: [ 4.0 , 0.2 , 12 , 0.1 ],
297 refrostStrength: [ 0.3 , 0 , 1 , 0.01 ],
298 resetFade: [ 400 , 50 , 2000 , 10 ],
299 } satisfies DialConfig ;
300
301 export const lightingDefaults = {
302 key: {
303 color: "#ffffff" ,
304 intensity: [ 3.2 , 0 , 12 , 0.05 ],
305 elevation: [ 62 , 10 , 89 , 0.5 ],
306 azimuth: [ 18 , - 90 , 90 , 0.5 ],
307 size: [ 1.0 , 0.2 , 3 , 0.01 ],
308 hoverBoost: [ 0.06 , 0 , 0.3 , 0.005 ],
309 },
310 fill: [ 0.18 , 0 , 1.5 , 0.005 ],
311 fillColor: "#ffffff" ,
312 fillGroundColor: "#262626" ,
313 rim: [ 0.9 , 0 , 5 , 0.01 ],
314 rimColor: "#ffffff" ,
315 rimElevation: [ 55 , 0 , 89 , 0.5 ],
316 shadow: {
317 intensity: [ 1.0 , 0 , 1 , 0.01 ],
318 softness: [ 6 , 0 , 24 , 0.25 ],
319 samples: [ 12 , 1 , 32 , 1 ],
320 mapSize: sel ( "2048" , [ "1024" , "2048" , "4096" ] as const ),
321 bias: [ - 0.0006 , - 0.005 , 0.005 , 0.0001 ],
322 },
323 sway: { amplitude: [ 0.03 , 0 , 0.15 , 0.001 ], period: [ 12 , 2 , 40 , 0.5 ] },
324 backdrop: {
325 top: "#2a2a2a" ,
326 mid: "#050505" ,
327 bottom: "#030303" ,
328 centerX: [ 0.5 , 0 , 1 , 0.005 ],
329 centerY: [ 1.02 , 0.5 , 1.5 , 0.005 ],
330 radius: [ 0.55 , 0.1 , 1.5 , 0.005 ],
331 falloff: [ 1.6 , 0.5 , 4 , 0.01 ],
332 noise: [ 1.0 , 0 , 3 , 0.05 ],
333 },
334 envSoftbox: [ 1.0 , 0 , 3 , 0.01 ],
335 envRim: [ 0.5 , 0 , 3 , 0.01 ],
336 envFill: [ 0.12 , 0 , 1 , 0.005 ],
337 } satisfies DialConfig ;
338
339 export const cameraDefaults = {
340 fov: [ 30 , 15 , 60 , 0.5 ],
341 distance: [ 13.5 , 4 , 24 , 0.05 ],
342 height: [ 1.2 , - 3 , 6 , 0.01 ],
343 lookAtY: [ - 0.15 , - 2 , 2 , 0.01 ],
344 parallaxRange: [ 3 , 0 , 10 , 0.1 ],
345 parallaxSmoothing: [ 0.8 , 0.05 , 3 , 0.01 ],
346 rotateYaw: [ 35 , 0 , 90 , 0.5 ],
347 rotatePitch: [ 10 , 0 , 45 , 0.5 ],
348 rotationLag: [ 1.2 , 0.05 , 4 , 0.01 ],
349 baseYaw: [ 0 , - 180 , 180 , 0.5 ],
350 basePitch: [ 0 , - 90 , 90 , 0.5 ],
351 idleDelay: [ 4 , 0 , 15 , 0.1 ],
352 idleAmplitude: [ 4 , 0 , 15 , 0.1 ],
353 idlePeriod: [ 24 , 4 , 80 , 0.5 ],
354 } satisfies DialConfig ;
355
356 export const postDefaults = {
357 dof: {
358 enabled: true ,
359 focusBias: [ 0 , - 3 , 3 , 0.01 ],
360 aperture: [ 0.9 , 0 , 4 , 0.01 ],
361 range: [ 3.0 , 0.2 , 12 , 0.05 ],
362 },
363 bloom: {
364 threshold: [ 1.2 , 0 , 3 , 0.01 ],
365 intensity: [ 0.15 , 0 , 1.5 , 0.005 ],
366 radius: [ 0.15 , 0 , 1 , 0.005 ],
367 },
368 monochrome: [ 1.0 , 0 , 1 , 0.01 ],
369 tonemap: sel ( "agx" , [ "agx" , "aces" , "neutral" , "linear" ] as const ),
370 exposure: [ 1.0 , 0.1 , 4 , 0.01 ],
371 contrast: [ 1.08 , 0.6 , 1.6 , 0.005 ],
372 blackLift: [ 0.02 , 0 , 0.1 , 0.001 ],
373 vignette: {
374 strength: [ 0.2 , 0 , 1 , 0.005 ],
375 softness: [ 0.7 , 0.1 , 1.5 , 0.005 ],
376 radius: [ 0.85 , 0.2 , 1.6 , 0.005 ],
377 },
378 grain: {
379 strength: [ 0.03 , 0 , 0.15 , 0.001 ],
380 size: [ 1 , 1 , 4 , 1 ],
381 speed: [ 1 , 0 , 3 , 0.05 ],
382 shadowWeight: [ 0.8 , 0 , 1 , 0.01 ],
383 // background-only grain against banding in recorded video: amplitude (0.04 = about 10 grey levels), block
384 // size in device pixels (use 3+ when the recording is downscaled), 0 speed = static (survives encoding best)
385 backgroundStrength: [ 0 , 0 , 0.25 , 0.001 ],
386 backgroundSize: [ 2 , 1 , 8 , 1 ],
387 backgroundSpeed: [ 0 , 0 , 3 , 0.05 ],
388 },
389 dither: true ,
390 aaMode: sel ( "traa" , [ "traa" , "none" ] as const ),
391 pixelRatioCap: [ 2 , 0.5 , 2 , 0.25 ],
392 } satisfies DialConfig ;
393
394 /** V2: real fragmentation. The object is pre-fractured into rigid chunks that fly out and come back. */
395 export const fractureDefaults = {
396 chunks: [ 600 , 40 , 3000 , 10 ],
397 seed: [ 3 , 0 , 999 , 1 ],
398 // a chunk breaks off when the brush passes within brushRadius (+ this share of the chunk's size)
399 hitPadding: [ 0.5 , 0 , 2 , 0.05 ],
400 eject: {
401 speed: [ 2.6 , 0 , 10 , 0.05 ],
402 spread: [ 0.7 , 0 , 4 , 0.05 ],
403 spin: [ 5 , 0 , 20 , 0.1 ],
404 jitter: [ 0.45 , 0 , 1 , 0.01 ],
405 backwardRatio: [ 0.2 , 0 , 1 , 0.01 ],
406 },
407 motion: {
408 drag: [ 1.3 , 0 , 8 , 0.01 ],
409 angularDrag: [ 1.6 , 0 , 8 , 0.01 ],
410 gravity: [ 0.3 , 0 , 3 , 0.01 ],
411 turbulence: [ 1.2 , 0 , 6 , 0.05 ],
412 turbulenceScale: [ 1.4 , 0.2 , 6 , 0.05 ],
413 maxSpeed: [ 10 , 1 , 40 , 0.1 ],
414 },
415 return: {
416 // a chunk turns back this long after it broke off (jittered), or sooner when the cursor is idle
417 returnAfter: [ 1.4 , 0 , 20 , 0.05 ],
418 returnJitter: [ 0.9 , 0 , 5 , 0.05 ],
419 idleDelay: [ 0.6 , 0 , 10 , 0.05 ],
420 spring: [ 14 , 0.5 , 60 , 0.1 ],
421 damping: [ 1.0 , 0.2 , 2 , 0.01 ],
422 ramp: [ 0.5 , 0 , 3 , 0.05 ],
423 rotationRate: [ 6 , 0.5 , 30 , 0.1 ],
424 snapDistance: [ 0.015 , 0.002 , 0.2 , 0.001 ],
425 },
426 caps: {
427 // inner (fracture) faces: fresh break = frosted, crumbly
428 frost: [ 0.85 , 0 , 1 , 0.01 ],
429 roughness: [ 0.8 , 0 , 1 , 0.01 ],
430 grain: [ 0.5 , 0 , 2 , 0.01 ],
431 darken: [ 0.15 , 0 , 1 , 0.01 ],
432 },
433 } satisfies DialConfig ;
434
435 export const performanceDefaults = {
436 // Dynamic resolution now scales the expensive scene/MRT pass. When nativePostEffects is on the
437 // final grade, vignette, grain and dither stay at the output DPR and only the 3D work is upscaled.
438 adaptiveResolution: true ,
439 targetFps: [ 60 , 30 , 120 , 1 ],
440 minPixelRatio: [ 0.75 , 0.5 , 2 , 0.05 ],
441 sceneResolutionScale: [ 1 , 0.5 , 1 , 0.05 ],
442 dynamicSceneResolution: true ,
443 minSceneResolutionScale: [ 0.35 , 0.25 , 1 , 0.05 ],
444 upscaler: sel ( "taau" , [ "taau" , "fsr1" , "bilinear" , "native" ] as const ),
445 upscaleSharpness: [ 0.2 , 0 , 1 , 0.05 ],
446 nativePostEffects: true ,
447 idleSkip: false , // skip field / particle / haze / density work while nothing is happening (lossless)
448 adaptiveSteps: false , // crack raymarch: fewer steps where the ice is thin (same step length as the thickest chord)
449 voronoiCells: sel ( "27" , [ "27" , "8" ] as const ), // 8 = jittered-lattice Voronoi, ~3x cheaper cracks, occasional phantom sheet
450 hazeResolution: sel ( "full" , [ "full" , "half" , "quarter" ] as const ),
451 turbulence: sel ( "full" , [ "full" , "fast" ] as const ), // fast = swirl from one noise sample instead of a 6-sample curl
452 gpuTimers: true ,
453 } satisfies DialConfig ;
454
455 export const debugDefaults = {
456 _collapsed: true ,
457 showErosionSlice: false ,
458 erosionSliceZ: [ 0.5 , 0 , 1 , 0.01 ],
459 showDensityGrid: false ,
460 showHitPoints: false ,
461 colorByState: false ,
462 colorBySize: false ,
463 colorByAge: false ,
464 wireframe: false ,
465 freeze: false ,
466 stepOnce: { type: "action" as const , label: "Step one frame" },
467 stats: true ,
468 forceStroke: false ,
469 } satisfies DialConfig ;
470
471 // ---------------------------------------------------------------------------------------------
472 // Baked overrides: paste a "Copy all params as JSON" export into ./baked.json and every default
473 // above is overridden at boot (tuple[0] / select default / plain value). Nothing persists otherwise.
474 import baked from "./baked.json" ;
475 function applyBaked ( cfg : any , values : any ) {
476 if ( ! values || typeof values !== "object" ) return ;
477 for ( const [ k , v ] of Object. entries (values)) {
478 if ( ! Object. hasOwn (cfg, k) || k. startsWith ( "_" )) continue ;
479 const c = cfg[k];
480 if (Array. isArray (c)) {
481 if ( typeof v === "number" ) c[ 0 ] = v;
482 } else if (c && typeof c === "object" && "type" in c) {
483 if ((c as any ).type !== "action" ) (c as any ).default = v;
484 } else if (c && typeof c === "object" ) applyBaked (c, v);
485 else cfg[k] = v;
486 }
487 }
488 /** Apply the baked values stored under `key` (e.g. a hero panel) to a DialKit config, in place. */
489 export function withBaked < T >( key : string , cfg : T ) : T {
490 applyBaked (cfg, (baked as any )[key]);
491 return cfg;
492 }
493 export const BAKED_FOLDERS : Record < string , any > = {
494 shape: shapeDefaults,
495 ice: iceDefaults,
496 erosion: erosionDefaults,
497 powder: powderDefaults,
498 healing: healingDefaults,
499 fracture: fractureDefaults,
500 lighting: lightingDefaults,
501 camera: cameraDefaults,
502 post: postDefaults,
503 performance: performanceDefaults,
504 debug: debugDefaults,
505 };
506 for ( const [ folder , cfg ] of Object. entries ( BAKED_FOLDERS )) applyBaked (cfg, (baked as any )[folder]);
507 if ( typeof location !== "undefined" && new URLSearchParams (location.search). has ( "idleskip" ))
508 (performanceDefaults as any ).idleSkip = true ;
509 if ( LITE ) fractureDefaults.chunks[ 0 ] = 120 ;
510 if ( LITE ) {
511 // headless / software-GPU checks: shrink everything heavy after the baked values are applied
512 shapeDefaults.segments[ 0 ] = 96 ;
513 iceDefaults.cracks.steps[ 0 ] = 6 ;
514 (iceDefaults.cracks as any ).fineCracks = false ;
515 iceDefaults.dispersion[ 0 ] = 0 ;
516 erosionDefaults.resolution.default = "64" ;
517 powderDefaults.particleCount.default = "100k" ;
518 powderDefaults.densityGrid.default = "32" ;
519 powderDefaults.hazeSteps[ 0 ] = 8 ;
520 }