git-hash-art 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ALGORITHM.md +105 -7
- package/CHANGELOG.md +8 -0
- package/dist/browser.js +241 -11
- package/dist/browser.js.map +1 -1
- package/dist/main.js +241 -11
- package/dist/main.js.map +1 -1
- package/dist/module.js +241 -11
- package/dist/module.js.map +1 -1
- package/package.json +1 -1
- package/src/lib/archetypes.ts +47 -3
- package/src/lib/canvas/colors.ts +25 -0
- package/src/lib/canvas/draw.ts +31 -1
- package/src/lib/canvas/shapes/affinity.ts +3 -3
- package/src/lib/render.ts +168 -7
package/ALGORITHM.md
CHANGED
|
@@ -9,10 +9,11 @@ Hash String
|
|
|
9
9
|
│
|
|
10
10
|
├─► Seed (mulberry32 PRNG)
|
|
11
11
|
│
|
|
12
|
-
├─► Archetype Selection (1 of 17 visual personalities)
|
|
12
|
+
├─► Archetype Selection (1 of 17 visual personalities, ~15% chance of blending two)
|
|
13
13
|
│
|
|
14
14
|
├─► Color Scheme (palette mode + temperature mode + contrast enforcement)
|
|
15
15
|
│ └─► Color Hierarchy (dominant 60% / secondary 25% / accent 15%)
|
|
16
|
+
│ └─► Per-Layer Palette Evolution (±20° hue drift across layers)
|
|
16
17
|
│
|
|
17
18
|
├─► Shape Palette (affinity-curated primary / supporting / accent shapes)
|
|
18
19
|
│
|
|
@@ -33,12 +34,15 @@ Hash String
|
|
|
33
34
|
2. Composition Mode Selection
|
|
34
35
|
2b. Symmetry Mode Selection (none / bilateral / quad)
|
|
35
36
|
3. Focal Points (rule-of-thirds biased) + Void Zones
|
|
37
|
+
3b. Void Zone Decoration (halos, scattered dots, concentric rings)
|
|
36
38
|
4. Flow Field Initialization
|
|
37
39
|
4b. Hero Shape (palette-aware, affinity-styled)
|
|
38
40
|
5. Shape Layers (× N layers, archetype-tuned)
|
|
39
41
|
│ ├─ Blend Mode (per-layer compositing)
|
|
40
42
|
│ ├─ Render Style (affinity-aware per shape)
|
|
41
43
|
│ ├─ Depth-of-Field (stroke thinning + contrast reduction on far layers)
|
|
44
|
+
│ ├─ Color Palette Evolution (per-layer hue drift via evolveHierarchy)
|
|
45
|
+
│ ├─ Focal Depth Boost (nesting/constellation chance ↑ near focal points)
|
|
42
46
|
│ ├─ Position (composition mode + focal bias + density check)
|
|
43
47
|
│ ├─ Shape Selection (palette-driven with size constraints)
|
|
44
48
|
│ ├─ Hero Avoidance Field (nearby shapes orient toward hero)
|
|
@@ -53,14 +57,18 @@ Hash String
|
|
|
53
57
|
│ ├─ 5d. Recursive Nesting (~15% of large shapes, palette-aware)
|
|
54
58
|
│ └─ 5e. Shape Constellations (~12% of large shapes, pre-composed groups)
|
|
55
59
|
6. Flow-Line Pass (variable color, pressure, branching)
|
|
56
|
-
6b.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
60
|
+
6b. Motion/Energy Lines (directional bursts from shapes)
|
|
61
|
+
6c. Layered Transparency / Glazing (~20% of shapes get multi-pass redraws)
|
|
62
|
+
7. Symmetry Mirroring (bilateral-x, bilateral-y, or quad)
|
|
63
|
+
7. Symmetry Mirroring (bilateral-x, bilateral-y, or quad)
|
|
64
|
+
8. Noise Texture Overlay
|
|
65
|
+
9. Vignette (radial edge darkening)
|
|
66
|
+
10. Organic Connecting Curves
|
|
67
|
+
11. Post-Processing
|
|
61
68
|
├─ Color Grading (unified tone overlay)
|
|
62
69
|
├─ Chromatic Aberration (neon/cosmic/ethereal only)
|
|
63
70
|
└─ Bloom (neon/cosmic only)
|
|
71
|
+
12. Signature Mark (deterministic geometric chop mark)
|
|
64
72
|
```
|
|
65
73
|
|
|
66
74
|
## 1. Deterministic RNG
|
|
@@ -126,6 +134,18 @@ Each archetype controls:
|
|
|
126
134
|
|
|
127
135
|
**celestial** — Boosts sacred geometry and cosmic shapes (crescent, geodesicDome, mandala, flowerOfLife, spirograph, fibonacciSpiral). Neon palette on dark background with heavy glow (2.5×) and deep layering (5 layers) creates a starfield-like composition with luminous geometric forms.
|
|
128
136
|
|
|
137
|
+
### Archetype Blending
|
|
138
|
+
|
|
139
|
+
~15% of hashes trigger **archetype blending**, where two archetypes are interpolated to create a hybrid personality. `blendArchetypes(primary, secondary, ratio)` works as follows:
|
|
140
|
+
|
|
141
|
+
- **Blend ratio:** 25–50% (the secondary archetype never dominates)
|
|
142
|
+
- **Numeric parameters** (`gridSize`, `layers`, `baseOpacity`, `minShapeSize`, etc.) are linearly interpolated between the two archetypes
|
|
143
|
+
- **Style arrays** (`preferredStyles`) are merged — the primary's styles come first, then any unique styles from the secondary
|
|
144
|
+
- **Categorical parameters** (`backgroundStyle`, `paletteMode`) use the primary's value when the blend ratio is below 50%, otherwise the secondary's
|
|
145
|
+
- **Boolean parameters** (`heroShape`) use the primary's value
|
|
146
|
+
|
|
147
|
+
This creates subtle hybrid personalities — e.g., a blend of `neon-glow` and `organic-flow` might produce glowing shapes with heavy flow lines and earth-toned neon outlines.
|
|
148
|
+
|
|
129
149
|
## 3. Color Scheme
|
|
130
150
|
|
|
131
151
|
Color generation uses the `color-scheme` library seeded from the hash, then applies archetype-specific palette modes.
|
|
@@ -173,6 +193,16 @@ The scheme detects whether the background leans warm or cool, then shifts foregr
|
|
|
173
193
|
|
|
174
194
|
Every shape color is checked against the background luminance. If the contrast ratio is too low, the color is lightened or darkened to ensure visibility.
|
|
175
195
|
|
|
196
|
+
### Color Palette Evolution
|
|
197
|
+
|
|
198
|
+
Colors aren't static across layers — they evolve. `evolveHierarchy(hierarchy, layerFraction)` applies a progressive hue rotation to the entire color hierarchy as rendering moves through layers:
|
|
199
|
+
|
|
200
|
+
- **Total drift:** ±20° across the full layer stack (direction determined by the dominant color's initial hue)
|
|
201
|
+
- **Per-layer rotation:** `layerFraction × 20°` applied via `hueRotate(color, degrees)`, which converts to HSL, shifts the hue, and converts back
|
|
202
|
+
- **Effect:** Early layers lean toward the original palette; later layers drift toward adjacent hues on the color wheel
|
|
203
|
+
|
|
204
|
+
This creates a subtle color journey across the depth of the image — warm reds in the background might shift toward orange-gold in the foreground, giving the composition a sense of temporal progression without breaking palette coherence.
|
|
205
|
+
|
|
176
206
|
## 4. Shape Affinity System
|
|
177
207
|
|
|
178
208
|
Not all shapes look equally good at all sizes or in all combinations. The affinity system replaces naive random shape selection with intentional curation.
|
|
@@ -297,6 +327,16 @@ Symmetry is applied by drawing the canvas image onto itself with `scale(-1, 1)`
|
|
|
297
327
|
|
|
298
328
|
1–2 **void zones** are placed randomly. Shapes landing inside a void zone have an 85% chance of being skipped, creating breathing room in the composition.
|
|
299
329
|
|
|
330
|
+
### Void Zone Decoration
|
|
331
|
+
|
|
332
|
+
Void zones aren't left completely empty — they receive subtle decorative elements that acknowledge the negative space:
|
|
333
|
+
|
|
334
|
+
- **Halo ring:** A thin circular stroke at the void zone's boundary using the accent color at 6–12% opacity
|
|
335
|
+
- **Scattered dots (~50% chance):** 5–12 tiny dots (1–3px) scattered randomly inside the void zone at 4–10% opacity, creating a dust-like texture
|
|
336
|
+
- **Inner concentric ring (~30% chance):** A smaller ring at 40–70% of the void zone radius at 3–8% opacity
|
|
337
|
+
|
|
338
|
+
These decorations are subtle enough to preserve the breathing room while preventing void zones from feeling like rendering errors.
|
|
339
|
+
|
|
300
340
|
## 8. Hero Shape
|
|
301
341
|
|
|
302
342
|
When the archetype enables `heroShape` and the RNG roll passes (60% chance), a dominant focal element is drawn at the first focal point:
|
|
@@ -321,6 +361,16 @@ The core of the image: `layers` passes (archetype-controlled, typically 2–5),
|
|
|
321
361
|
- **Atmospheric desaturation:** Later layers are progressively desaturated (up to 30%) to simulate depth
|
|
322
362
|
- **Depth-of-field:** Later layers get thinner strokes (down to 40% of base width) and reduced contrast (up to 20% opacity reduction), simulating camera focus falloff
|
|
323
363
|
|
|
364
|
+
### Focal Depth Boost
|
|
365
|
+
|
|
366
|
+
Shapes near focal points receive enhanced detail via `focalDetailBoost()`:
|
|
367
|
+
|
|
368
|
+
- **Nesting chance:** Boosted from the base 15% up to 15–30% for shapes within 25% of canvas size from a focal point
|
|
369
|
+
- **Constellation chance:** Boosted from the base 12% up to 12–22% near focal points
|
|
370
|
+
- **Boost strength:** Proportional to proximity — shapes right at the focal point get the full boost, shapes at the edge of the influence radius get minimal boost
|
|
371
|
+
|
|
372
|
+
This creates a natural depth-of-detail effect where the areas your eye is drawn to (focal points) contain the most intricate shape compositions.
|
|
373
|
+
|
|
324
374
|
### Per-Shape Pipeline
|
|
325
375
|
|
|
326
376
|
For each shape in a layer:
|
|
@@ -379,7 +429,7 @@ Each member shape uses hierarchy colors with HSL jitter and affinity-aware rende
|
|
|
379
429
|
|
|
380
430
|
## 10. Render Styles
|
|
381
431
|
|
|
382
|
-
Each shape is drawn using one of
|
|
432
|
+
Each shape is drawn using one of 15 render styles:
|
|
383
433
|
|
|
384
434
|
| Style | Description |
|
|
385
435
|
| ----- | ----------- |
|
|
@@ -397,6 +447,7 @@ Each shape is drawn using one of 14 render styles:
|
|
|
397
447
|
| wood-grain | 20% base tint + clipped parallel wavy lines at a random angle, simulating wood texture |
|
|
398
448
|
| marble-vein | 35% soft base + clipped branching vein lines that drift and fork, simulating marble stone |
|
|
399
449
|
| fabric-weave | 15% ghost base + clipped interlocking horizontal and vertical thread lines at alternating opacities |
|
|
450
|
+
| hand-drawn | Fill + 2–3 wobbly offset stroke passes simulating a hand-drawn sketch |
|
|
400
451
|
|
|
401
452
|
### Texture Fill Details
|
|
402
453
|
|
|
@@ -414,6 +465,38 @@ The 4 new texture fills (noise-grain, wood-grain, marble-vein, fabric-weave) all
|
|
|
414
465
|
|
|
415
466
|
**fabric-weave** draws horizontal threads at full spacing, then vertical threads at half-spacing offsets, creating an over-under weave pattern. The two thread directions use different opacities (55% vs 45%) and colors (stroke vs fill) for visual distinction.
|
|
416
467
|
|
|
468
|
+
### Hand-Drawn Style
|
|
469
|
+
|
|
470
|
+
The `hand-drawn` style simulates a sketchy, imprecise outline:
|
|
471
|
+
|
|
472
|
+
1. **Base fill:** Standard fill of the shape
|
|
473
|
+
2. **Wobbly strokes (2–3 passes):** Each pass redraws the shape's outline with a small random offset (1–3px) and slightly varied line width, creating the impression of a hand tracing the same line multiple times
|
|
474
|
+
3. **Opacity variation:** Each pass uses 40–70% opacity so the overlapping strokes build up naturally
|
|
475
|
+
|
|
476
|
+
This style works particularly well on organic shapes (circle, triangle, blob) where the wobble feels intentional rather than broken.
|
|
477
|
+
|
|
478
|
+
### Layered Transparency / Glazing
|
|
479
|
+
|
|
480
|
+
~20% of shapes receive **glazing** — 2–3 additional fill-only passes drawn immediately after the main shape:
|
|
481
|
+
|
|
482
|
+
- Each pass is progressively smaller (85% → 72% → 61% of the original size)
|
|
483
|
+
- Each pass is progressively more opaque (adding 5–10% opacity per pass)
|
|
484
|
+
- Only the fill is redrawn (no stroke), creating a pigment-pooling effect where the center of the shape appears richer and more saturated
|
|
485
|
+
|
|
486
|
+
This simulates the traditional oil painting technique of building up translucent layers to create depth and luminosity.
|
|
487
|
+
|
|
488
|
+
### Motion / Energy Lines
|
|
489
|
+
|
|
490
|
+
Short directional line bursts radiate from shapes to suggest movement and energy:
|
|
491
|
+
|
|
492
|
+
- **Trigger:** Always for `dense-chaotic`, `cosmic`, `neon-glow`, and `bold-graphic` archetypes; ~25% random chance for others
|
|
493
|
+
- **Count:** 3–6 lines per shape
|
|
494
|
+
- **Length:** 8–20% of the shape's size
|
|
495
|
+
- **Direction:** Radiating outward from the shape center at evenly-spaced angles with ±15° jitter
|
|
496
|
+
- **Style:** Thin lines (0.5–1.5px) using the shape's stroke color at 15–35% opacity, tapering toward the ends
|
|
497
|
+
|
|
498
|
+
Motion lines are drawn after the main shape layers but before symmetry mirroring, so they participate in any bilateral/quad reflections.
|
|
499
|
+
|
|
417
500
|
### Watercolor Detail
|
|
418
501
|
|
|
419
502
|
The watercolor style simulates wet media through 4 passes:
|
|
@@ -461,3 +544,18 @@ Only for `neon-glow`, `cosmic`, and `ethereal` archetypes. The canvas is drawn o
|
|
|
461
544
|
### Bloom
|
|
462
545
|
|
|
463
546
|
Only for `neon-glow` and `cosmic` archetypes. The canvas is redrawn with `shadowBlur` at 30px (scaled) and white shadow color, composited via `screen` at 8% opacity. This creates a soft glow around bright areas.
|
|
547
|
+
|
|
548
|
+
## 13. Signature Mark
|
|
549
|
+
|
|
550
|
+
The final rendering step places a small deterministic **chop mark** (inspired by East Asian seal stamps) in the bottom-right corner of the canvas:
|
|
551
|
+
|
|
552
|
+
- **RNG isolation:** Uses a separate RNG seeded with a salt of 42, so the signature is independent of all other rendering decisions
|
|
553
|
+
- **Position:** Bottom-right corner with a small margin (3% of canvas size)
|
|
554
|
+
- **Size:** 1.5–2.5% of the shorter canvas dimension
|
|
555
|
+
- **Structure:**
|
|
556
|
+
1. Outer circle ring (stroke-only) at 12–20% opacity
|
|
557
|
+
2. 2–4 inner lines crossing the circle at unique angles derived from the hash, creating a distinctive geometric pattern
|
|
558
|
+
3. Center dot at slightly higher opacity
|
|
559
|
+
- **Color:** Uses the accent color from the hierarchy at very low opacity so it's visible but never distracting
|
|
560
|
+
|
|
561
|
+
The signature is unique per hash (different inner line patterns) but always recognizable as a chop mark, giving each generated image a subtle artist's stamp.
|
package/CHANGELOG.md
CHANGED
|
@@ -4,12 +4,20 @@ All notable changes to this project will be documented in this file. Dates are d
|
|
|
4
4
|
|
|
5
5
|
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
6
6
|
|
|
7
|
+
#### [0.10.0](https://github.com/gfargo/git-hash-art/compare/0.9.0...0.10.0)
|
|
8
|
+
|
|
9
|
+
- feat: color palette evolution, edge treatment, glazing, motion lines, archetype blending, focal depth, signature mark [`#17`](https://github.com/gfargo/git-hash-art/pull/17)
|
|
10
|
+
- feat: color palette evolution, edge treatment, negative space, glazing, motion lines, archetype blending, focal depth, signature mark [`b496771`](https://github.com/gfargo/git-hash-art/commit/b49677108ae170fef9c1108d40d7db6995efb48c)
|
|
11
|
+
|
|
7
12
|
#### [0.9.0](https://github.com/gfargo/git-hash-art/compare/0.8.0...0.9.0)
|
|
8
13
|
|
|
14
|
+
> 19 March 2026
|
|
15
|
+
|
|
9
16
|
- feat: new archetypes, depth-of-field, texture fills, constellations, background patterns [`#16`](https://github.com/gfargo/git-hash-art/pull/16)
|
|
10
17
|
- docs: update ALGORITHM.md with visual quality improvements [`#15`](https://github.com/gfargo/git-hash-art/pull/15)
|
|
11
18
|
- feat: add 4 archetypes, depth-of-field, texture fills, constellations, background patterns [`7f9b0fc`](https://github.com/gfargo/git-hash-art/commit/7f9b0fcbc4a17272dd6a62371e037fb0555c830b)
|
|
12
19
|
- add version comparison script [`9301176`](https://github.com/gfargo/git-hash-art/commit/9301176b4f1fe0af85d5d7f85e58db9be10ec382)
|
|
20
|
+
- chore: release v0.9.0 [`32311c2`](https://github.com/gfargo/git-hash-art/commit/32311c2fda12dbcb388e77d9e2faba380d3b0c29)
|
|
13
21
|
|
|
14
22
|
#### [0.8.0](https://github.com/gfargo/git-hash-art/compare/0.7.0...0.8.0)
|
|
15
23
|
|
package/dist/browser.js
CHANGED
|
@@ -518,6 +518,18 @@ function $b5a262d09b87e373$export$6d1620b367f86f7a(rng) {
|
|
|
518
518
|
intensity: intensity
|
|
519
519
|
};
|
|
520
520
|
}
|
|
521
|
+
function $b5a262d09b87e373$export$1793a1bfbe4f6ff5(hex, degrees) {
|
|
522
|
+
const [h, s, l] = $b5a262d09b87e373$var$hexToHsl(hex);
|
|
523
|
+
return $b5a262d09b87e373$var$hslToHex((h + degrees + 360) % 360, s, l);
|
|
524
|
+
}
|
|
525
|
+
function $b5a262d09b87e373$export$703ba40a4347f77a(base, layerRatio, hueShiftPerLayer) {
|
|
526
|
+
const shift = layerRatio * hueShiftPerLayer;
|
|
527
|
+
return {
|
|
528
|
+
dominant: $b5a262d09b87e373$export$1793a1bfbe4f6ff5(base.dominant, shift),
|
|
529
|
+
secondary: $b5a262d09b87e373$export$1793a1bfbe4f6ff5(base.secondary, shift * 0.7),
|
|
530
|
+
accent: $b5a262d09b87e373$export$1793a1bfbe4f6ff5(base.accent, shift * 0.5)
|
|
531
|
+
};
|
|
532
|
+
}
|
|
521
533
|
|
|
522
534
|
|
|
523
535
|
|
|
@@ -1801,7 +1813,8 @@ const $e0f99502ff383dd8$var$RENDER_STYLES = [
|
|
|
1801
1813
|
"noise-grain",
|
|
1802
1814
|
"wood-grain",
|
|
1803
1815
|
"marble-vein",
|
|
1804
|
-
"fabric-weave"
|
|
1816
|
+
"fabric-weave",
|
|
1817
|
+
"hand-drawn"
|
|
1805
1818
|
];
|
|
1806
1819
|
function $e0f99502ff383dd8$export$9fd4e64b2acd410e(rng) {
|
|
1807
1820
|
return $e0f99502ff383dd8$var$RENDER_STYLES[Math.floor(rng() * $e0f99502ff383dd8$var$RENDER_STYLES.length)];
|
|
@@ -2182,6 +2195,33 @@ function $e0f99502ff383dd8$export$71b514a25c47df50(ctx, shape, x, y, config) {
|
|
|
2182
2195
|
ctx.globalAlpha /= 0.3;
|
|
2183
2196
|
break;
|
|
2184
2197
|
}
|
|
2198
|
+
case "hand-drawn":
|
|
2199
|
+
{
|
|
2200
|
+
// Wobbly hand-drawn edge treatment — fill normally, then redraw
|
|
2201
|
+
// the outline with perturbed control points for a sketchy feel
|
|
2202
|
+
const savedAlphaHD = ctx.globalAlpha;
|
|
2203
|
+
ctx.globalAlpha = savedAlphaHD * 0.85;
|
|
2204
|
+
ctx.fill();
|
|
2205
|
+
ctx.globalAlpha = savedAlphaHD;
|
|
2206
|
+
// Draw 2-3 slightly offset wobbly strokes for a sketchy look
|
|
2207
|
+
const wobblePasses = 2 + (rng ? Math.floor(rng() * 2) : 0);
|
|
2208
|
+
ctx.lineWidth = strokeWidth * 0.8;
|
|
2209
|
+
for(let wp = 0; wp < wobblePasses; wp++){
|
|
2210
|
+
ctx.globalAlpha = savedAlphaHD * (0.4 - wp * 0.1);
|
|
2211
|
+
ctx.save();
|
|
2212
|
+
// Slight random offset per pass
|
|
2213
|
+
const wobbleX = rng ? (rng() - 0.5) * size * 0.02 : 0;
|
|
2214
|
+
const wobbleY = rng ? (rng() - 0.5) * size * 0.02 : 0;
|
|
2215
|
+
ctx.translate(wobbleX, wobbleY);
|
|
2216
|
+
// Slightly different scale per pass for edge variation
|
|
2217
|
+
const wobbleScale = 1 + (rng ? (rng() - 0.5) * 0.03 : 0);
|
|
2218
|
+
ctx.scale(wobbleScale, wobbleScale);
|
|
2219
|
+
ctx.stroke();
|
|
2220
|
+
ctx.restore();
|
|
2221
|
+
}
|
|
2222
|
+
ctx.globalAlpha = savedAlphaHD;
|
|
2223
|
+
break;
|
|
2224
|
+
}
|
|
2185
2225
|
case "fill-and-stroke":
|
|
2186
2226
|
default:
|
|
2187
2227
|
ctx.fill();
|
|
@@ -2317,7 +2357,8 @@ const $8286059160ee2e04$export$4343b39fe47bd82c = {
|
|
|
2317
2357
|
bestStyles: [
|
|
2318
2358
|
"fill-only",
|
|
2319
2359
|
"watercolor",
|
|
2320
|
-
"fill-and-stroke"
|
|
2360
|
+
"fill-and-stroke",
|
|
2361
|
+
"hand-drawn"
|
|
2321
2362
|
]
|
|
2322
2363
|
},
|
|
2323
2364
|
square: {
|
|
@@ -2354,7 +2395,8 @@ const $8286059160ee2e04$export$4343b39fe47bd82c = {
|
|
|
2354
2395
|
bestStyles: [
|
|
2355
2396
|
"fill-and-stroke",
|
|
2356
2397
|
"fill-only",
|
|
2357
|
-
"watercolor"
|
|
2398
|
+
"watercolor",
|
|
2399
|
+
"hand-drawn"
|
|
2358
2400
|
]
|
|
2359
2401
|
},
|
|
2360
2402
|
hexagon: {
|
|
@@ -2734,7 +2776,8 @@ const $8286059160ee2e04$export$4343b39fe47bd82c = {
|
|
|
2734
2776
|
bestStyles: [
|
|
2735
2777
|
"fill-only",
|
|
2736
2778
|
"watercolor",
|
|
2737
|
-
"fill-and-stroke"
|
|
2779
|
+
"fill-and-stroke",
|
|
2780
|
+
"hand-drawn"
|
|
2738
2781
|
]
|
|
2739
2782
|
},
|
|
2740
2783
|
ngon: {
|
|
@@ -3614,8 +3657,51 @@ const $68a238ccd77f2bcd$var$ARCHETYPES = [
|
|
|
3614
3657
|
invertForeground: false
|
|
3615
3658
|
}
|
|
3616
3659
|
];
|
|
3660
|
+
/**
|
|
3661
|
+
* Linearly interpolate between two archetype numeric parameters.
|
|
3662
|
+
*/ function $68a238ccd77f2bcd$var$lerpNum(a, b, t) {
|
|
3663
|
+
return a + (b - a) * t;
|
|
3664
|
+
}
|
|
3665
|
+
/**
|
|
3666
|
+
* Blend two archetypes by interpolating their numeric parameters
|
|
3667
|
+
* and merging their style arrays.
|
|
3668
|
+
*/ function $68a238ccd77f2bcd$var$blendArchetypes(a, b, t) {
|
|
3669
|
+
// Merge preferred styles — unique union, primary archetype first
|
|
3670
|
+
const mergedStyles = [
|
|
3671
|
+
...new Set([
|
|
3672
|
+
...a.preferredStyles,
|
|
3673
|
+
...b.preferredStyles
|
|
3674
|
+
])
|
|
3675
|
+
];
|
|
3676
|
+
return {
|
|
3677
|
+
name: `${a.name}+${b.name}`,
|
|
3678
|
+
gridSize: Math.round($68a238ccd77f2bcd$var$lerpNum(a.gridSize, b.gridSize, t)),
|
|
3679
|
+
layers: Math.round($68a238ccd77f2bcd$var$lerpNum(a.layers, b.layers, t)),
|
|
3680
|
+
baseOpacity: $68a238ccd77f2bcd$var$lerpNum(a.baseOpacity, b.baseOpacity, t),
|
|
3681
|
+
opacityReduction: $68a238ccd77f2bcd$var$lerpNum(a.opacityReduction, b.opacityReduction, t),
|
|
3682
|
+
minShapeSize: Math.round($68a238ccd77f2bcd$var$lerpNum(a.minShapeSize, b.minShapeSize, t)),
|
|
3683
|
+
maxShapeSize: Math.round($68a238ccd77f2bcd$var$lerpNum(a.maxShapeSize, b.maxShapeSize, t)),
|
|
3684
|
+
backgroundStyle: t < 0.5 ? a.backgroundStyle : b.backgroundStyle,
|
|
3685
|
+
paletteMode: t < 0.5 ? a.paletteMode : b.paletteMode,
|
|
3686
|
+
preferredStyles: mergedStyles,
|
|
3687
|
+
flowLineMultiplier: $68a238ccd77f2bcd$var$lerpNum(a.flowLineMultiplier, b.flowLineMultiplier, t),
|
|
3688
|
+
heroShape: t < 0.5 ? a.heroShape : b.heroShape,
|
|
3689
|
+
glowMultiplier: $68a238ccd77f2bcd$var$lerpNum(a.glowMultiplier, b.glowMultiplier, t),
|
|
3690
|
+
sizePower: $68a238ccd77f2bcd$var$lerpNum(a.sizePower, b.sizePower, t),
|
|
3691
|
+
invertForeground: t < 0.5 ? a.invertForeground : b.invertForeground
|
|
3692
|
+
};
|
|
3693
|
+
}
|
|
3617
3694
|
function $68a238ccd77f2bcd$export$f1142fd7da4d6590(rng) {
|
|
3618
|
-
|
|
3695
|
+
const primary = $68a238ccd77f2bcd$var$ARCHETYPES[Math.floor(rng() * $68a238ccd77f2bcd$var$ARCHETYPES.length)];
|
|
3696
|
+
// ~15% chance of blending with a second archetype
|
|
3697
|
+
if (rng() < 0.15) {
|
|
3698
|
+
const secondary = $68a238ccd77f2bcd$var$ARCHETYPES[Math.floor(rng() * $68a238ccd77f2bcd$var$ARCHETYPES.length)];
|
|
3699
|
+
if (secondary.name !== primary.name) {
|
|
3700
|
+
const blendT = 0.25 + rng() * 0.25; // 25-50% blend toward secondary
|
|
3701
|
+
return $68a238ccd77f2bcd$var$blendArchetypes(primary, secondary, blendT);
|
|
3702
|
+
}
|
|
3703
|
+
}
|
|
3704
|
+
return primary;
|
|
3619
3705
|
}
|
|
3620
3706
|
|
|
3621
3707
|
|
|
@@ -3951,6 +4037,8 @@ function $1f63dc64b5593c73$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
3951
4037
|
const colorGrade = (0, $b5a262d09b87e373$export$6d1620b367f86f7a)(rng);
|
|
3952
4038
|
// ── 0e. Light direction — consistent shadow angle ──────────────
|
|
3953
4039
|
const lightAngle = rng() * Math.PI * 2;
|
|
4040
|
+
// ── 0f. Palette evolution — hue drift direction across layers ──
|
|
4041
|
+
const paletteHueShift = (rng() - 0.5) * 40; // -20° to +20° total drift
|
|
3954
4042
|
const scaleFactor = Math.min(width, height) / 1024;
|
|
3955
4043
|
const adjustedMinSize = minShapeSize * scaleFactor;
|
|
3956
4044
|
const adjustedMaxSize = maxShapeSize * scaleFactor;
|
|
@@ -4125,6 +4213,41 @@ function $1f63dc64b5593c73$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4125
4213
|
ry + (nearest.y - ry) * pull
|
|
4126
4214
|
];
|
|
4127
4215
|
}
|
|
4216
|
+
// ── 3b. Void zone decoration — intentional negative space ────
|
|
4217
|
+
for (const zone of voidZones){
|
|
4218
|
+
// Subtle halo ring around void zones
|
|
4219
|
+
ctx.globalAlpha = 0.04 + rng() * 0.04;
|
|
4220
|
+
ctx.strokeStyle = (0, $b5a262d09b87e373$export$f2121afcad3d553f)(colorHierarchy.accent, 0.2);
|
|
4221
|
+
ctx.lineWidth = 1.5 * scaleFactor;
|
|
4222
|
+
ctx.beginPath();
|
|
4223
|
+
ctx.arc(zone.x, zone.y, zone.radius, 0, Math.PI * 2);
|
|
4224
|
+
ctx.stroke();
|
|
4225
|
+
// ~50% chance: scatter tiny dots inside the void
|
|
4226
|
+
if (rng() < 0.5) {
|
|
4227
|
+
const dotCount = 3 + Math.floor(rng() * 6);
|
|
4228
|
+
ctx.globalAlpha = 0.06 + rng() * 0.04;
|
|
4229
|
+
ctx.fillStyle = (0, $b5a262d09b87e373$export$f2121afcad3d553f)(colorHierarchy.secondary, 0.15);
|
|
4230
|
+
for(let d = 0; d < dotCount; d++){
|
|
4231
|
+
const angle = rng() * Math.PI * 2;
|
|
4232
|
+
const dist = rng() * zone.radius * 0.7;
|
|
4233
|
+
const dotR = (1 + rng() * 3) * scaleFactor;
|
|
4234
|
+
ctx.beginPath();
|
|
4235
|
+
ctx.arc(zone.x + Math.cos(angle) * dist, zone.y + Math.sin(angle) * dist, dotR, 0, Math.PI * 2);
|
|
4236
|
+
ctx.fill();
|
|
4237
|
+
}
|
|
4238
|
+
}
|
|
4239
|
+
// ~30% chance: thin concentric ring inside
|
|
4240
|
+
if (rng() < 0.3) {
|
|
4241
|
+
ctx.globalAlpha = 0.03 + rng() * 0.03;
|
|
4242
|
+
ctx.strokeStyle = (0, $b5a262d09b87e373$export$f2121afcad3d553f)(colorHierarchy.dominant, 0.1);
|
|
4243
|
+
ctx.lineWidth = 0.5 * scaleFactor;
|
|
4244
|
+
const innerR = zone.radius * (0.4 + rng() * 0.3);
|
|
4245
|
+
ctx.beginPath();
|
|
4246
|
+
ctx.arc(zone.x, zone.y, innerR, 0, Math.PI * 2);
|
|
4247
|
+
ctx.stroke();
|
|
4248
|
+
}
|
|
4249
|
+
}
|
|
4250
|
+
ctx.globalAlpha = 1;
|
|
4128
4251
|
// ── 4. Flow field seed values ──────────────────────────────────
|
|
4129
4252
|
const fieldAngleBase = rng() * Math.PI * 2;
|
|
4130
4253
|
const fieldFreq = 0.5 + rng() * 2;
|
|
@@ -4197,6 +4320,18 @@ function $1f63dc64b5593c73$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4197
4320
|
const dofFactor = 1 - layerRatio * 0.5; // 1.0 for front layer, 0.5 for back
|
|
4198
4321
|
const dofStrokeScale = 0.4 + dofFactor * 0.6; // strokes thin out with depth
|
|
4199
4322
|
const dofContrastReduction = layerRatio * 0.2; // colors fade toward bg
|
|
4323
|
+
// Color palette evolution — hue-rotate the hierarchy per layer
|
|
4324
|
+
const layerHierarchy = (0, $b5a262d09b87e373$export$703ba40a4347f77a)(colorHierarchy, layerRatio, paletteHueShift);
|
|
4325
|
+
// Focal depth: shapes near focal points get more detail
|
|
4326
|
+
const focalDetailBoost = (px, py)=>{
|
|
4327
|
+
let minFocalDist = Infinity;
|
|
4328
|
+
for (const fp of focalPoints){
|
|
4329
|
+
const d = Math.hypot(px - fp.x, py - fp.y);
|
|
4330
|
+
if (d < minFocalDist) minFocalDist = d;
|
|
4331
|
+
}
|
|
4332
|
+
const maxDist = Math.hypot(width, height) * 0.5;
|
|
4333
|
+
return Math.max(0, 1 - minFocalDist / maxDist); // 1.0 at focal, 0.0 at edges
|
|
4334
|
+
};
|
|
4200
4335
|
for(let i = 0; i < numShapes; i++){
|
|
4201
4336
|
// Position from composition mode, then focal bias
|
|
4202
4337
|
const rawPos = $1f63dc64b5593c73$var$getCompositionPosition(compositionMode, rng, width, height, i, numShapes, cx, cy);
|
|
@@ -4227,9 +4362,9 @@ function $1f63dc64b5593c73$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4227
4362
|
rotation = rotation + (angleToHero - rotation) * blendFactor * 0.4;
|
|
4228
4363
|
}
|
|
4229
4364
|
}
|
|
4230
|
-
// Positional color from hierarchy + jitter
|
|
4231
|
-
let fillBase = $1f63dc64b5593c73$var$getPositionalColor(x, y, width, height,
|
|
4232
|
-
const strokeBase = (0, $b5a262d09b87e373$export$b49f62f0a99da0e8)(
|
|
4365
|
+
// Positional color from hierarchy + jitter (using evolved layer palette)
|
|
4366
|
+
let fillBase = $1f63dc64b5593c73$var$getPositionalColor(x, y, width, height, layerHierarchy, rng);
|
|
4367
|
+
const strokeBase = (0, $b5a262d09b87e373$export$b49f62f0a99da0e8)(layerHierarchy, rng);
|
|
4233
4368
|
// Desaturate colors on later layers for depth
|
|
4234
4369
|
if (atmosphericDesat > 0) fillBase = (0, $b5a262d09b87e373$export$fb75607d98509d9)(fillBase, atmosphericDesat);
|
|
4235
4370
|
// Temperature contrast: shift foreground shapes opposite to background
|
|
@@ -4321,6 +4456,25 @@ function $1f63dc64b5593c73$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4321
4456
|
mirrorGap: size * (0.1 + rng() * 0.3)
|
|
4322
4457
|
});
|
|
4323
4458
|
else (0, $e0f99502ff383dd8$export$bb35a6995ddbf32d)(ctx, shape, finalX, finalY, shapeConfig);
|
|
4459
|
+
// ── Glazing — luminous multi-pass transparency on ~20% of shapes ──
|
|
4460
|
+
if (rng() < 0.2 && size > adjustedMinSize * 2) {
|
|
4461
|
+
const glazePasses = 2 + Math.floor(rng() * 2);
|
|
4462
|
+
for(let g = 0; g < glazePasses; g++){
|
|
4463
|
+
const glazeScale = 1 - (g + 1) * 0.12; // progressively smaller
|
|
4464
|
+
const glazeAlpha = 0.08 + g * 0.04; // progressively more opaque toward center
|
|
4465
|
+
ctx.globalAlpha = glazeAlpha;
|
|
4466
|
+
(0, $e0f99502ff383dd8$export$bb35a6995ddbf32d)(ctx, shape, finalX, finalY, {
|
|
4467
|
+
fillColor: (0, $b5a262d09b87e373$export$f2121afcad3d553f)(fillColor, 0.15 + g * 0.1),
|
|
4468
|
+
strokeColor: "rgba(0,0,0,0)",
|
|
4469
|
+
strokeWidth: 0,
|
|
4470
|
+
size: size * glazeScale,
|
|
4471
|
+
rotation: rotation,
|
|
4472
|
+
proportionType: "GOLDEN_RATIO",
|
|
4473
|
+
renderStyle: "fill-only",
|
|
4474
|
+
rng: rng
|
|
4475
|
+
});
|
|
4476
|
+
}
|
|
4477
|
+
}
|
|
4324
4478
|
shapePositions.push({
|
|
4325
4479
|
x: finalX,
|
|
4326
4480
|
y: finalY,
|
|
@@ -4358,7 +4512,10 @@ function $1f63dc64b5593c73$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4358
4512
|
}
|
|
4359
4513
|
}
|
|
4360
4514
|
// ── 5d. Recursive nesting ──────────────────────────────────
|
|
4361
|
-
|
|
4515
|
+
// Focal depth: shapes near focal points get more detail
|
|
4516
|
+
const focalProximity = focalDetailBoost(finalX, finalY);
|
|
4517
|
+
const nestingChance = 0.15 + focalProximity * 0.15; // 15-30% near focal
|
|
4518
|
+
if (size > adjustedMaxSize * 0.4 && rng() < nestingChance) {
|
|
4362
4519
|
const innerCount = 1 + Math.floor(rng() * 3);
|
|
4363
4520
|
for(let n = 0; n < innerCount; n++){
|
|
4364
4521
|
// Pick inner shape from palette affinities
|
|
@@ -4383,7 +4540,8 @@ function $1f63dc64b5593c73$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4383
4540
|
}
|
|
4384
4541
|
}
|
|
4385
4542
|
// ── 5e. Shape constellations — pre-composed groups ─────────
|
|
4386
|
-
|
|
4543
|
+
const constellationChance = 0.12 + focalProximity * 0.1; // 12-22% near focal
|
|
4544
|
+
if (size > adjustedMaxSize * 0.35 && rng() < constellationChance) {
|
|
4387
4545
|
const constellation = $1f63dc64b5593c73$var$CONSTELLATIONS[Math.floor(rng() * $1f63dc64b5593c73$var$CONSTELLATIONS.length)];
|
|
4388
4546
|
const members = constellation.build(rng, size);
|
|
4389
4547
|
const groupRotation = rng() * Math.PI * 2;
|
|
@@ -4487,7 +4645,41 @@ function $1f63dc64b5593c73$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4487
4645
|
prevY = fy;
|
|
4488
4646
|
}
|
|
4489
4647
|
}
|
|
4490
|
-
// ── 6b.
|
|
4648
|
+
// ── 6b. Motion/energy lines — short directional bursts ─────────
|
|
4649
|
+
const energyArchetypes = [
|
|
4650
|
+
"dense-chaotic",
|
|
4651
|
+
"cosmic",
|
|
4652
|
+
"neon-glow",
|
|
4653
|
+
"bold-graphic"
|
|
4654
|
+
];
|
|
4655
|
+
const hasEnergyLines = energyArchetypes.some((a)=>archetype.name.includes(a)) || rng() < 0.25;
|
|
4656
|
+
if (hasEnergyLines && shapePositions.length > 0) {
|
|
4657
|
+
const energyCount = 5 + Math.floor(rng() * 10);
|
|
4658
|
+
ctx.lineCap = "round";
|
|
4659
|
+
for(let e = 0; e < energyCount; e++){
|
|
4660
|
+
// Pick a random shape to radiate from
|
|
4661
|
+
const source = shapePositions[Math.floor(rng() * shapePositions.length)];
|
|
4662
|
+
const burstCount = 2 + Math.floor(rng() * 4);
|
|
4663
|
+
const baseAngle = flowAngle(source.x, source.y);
|
|
4664
|
+
for(let b = 0; b < burstCount; b++){
|
|
4665
|
+
const angle = baseAngle + (rng() - 0.5) * 1.2;
|
|
4666
|
+
const lineLen = (source.size * 0.3 + rng() * source.size * 0.5) * scaleFactor * 0.3;
|
|
4667
|
+
const startDist = source.size * 0.5;
|
|
4668
|
+
const sx = source.x + Math.cos(angle) * startDist;
|
|
4669
|
+
const sy = source.y + Math.sin(angle) * startDist;
|
|
4670
|
+
const ex = sx + Math.cos(angle) * lineLen;
|
|
4671
|
+
const ey = sy + Math.sin(angle) * lineLen;
|
|
4672
|
+
ctx.globalAlpha = 0.04 + rng() * 0.06;
|
|
4673
|
+
ctx.strokeStyle = (0, $b5a262d09b87e373$export$f2121afcad3d553f)((0, $b5a262d09b87e373$export$90ad0e6170cf6af5)((0, $b5a262d09b87e373$export$b49f62f0a99da0e8)(colorHierarchy, rng), bgLum), 0.3);
|
|
4674
|
+
ctx.lineWidth = (0.5 + rng() * 1.5) * scaleFactor;
|
|
4675
|
+
ctx.beginPath();
|
|
4676
|
+
ctx.moveTo(sx, sy);
|
|
4677
|
+
ctx.lineTo(ex, ey);
|
|
4678
|
+
ctx.stroke();
|
|
4679
|
+
}
|
|
4680
|
+
}
|
|
4681
|
+
}
|
|
4682
|
+
// ── 6c. Apply symmetry mirroring ─────────────────────────────────
|
|
4491
4683
|
if (symmetryMode !== "none") {
|
|
4492
4684
|
const canvas = ctx.canvas;
|
|
4493
4685
|
ctx.save();
|
|
@@ -4598,6 +4790,44 @@ function $1f63dc64b5593c73$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4598
4790
|
ctx.restore();
|
|
4599
4791
|
ctx.globalCompositeOperation = "source-over";
|
|
4600
4792
|
}
|
|
4793
|
+
// ── 11. Signature mark — unique geometric chop from hash prefix ──
|
|
4794
|
+
{
|
|
4795
|
+
const sigRng = (0, $616009579e3d72c5$export$eaf9227667332084)((0, $616009579e3d72c5$export$e9cc707de01b7042)(gitHash, 42));
|
|
4796
|
+
const sigSize = Math.min(width, height) * 0.025;
|
|
4797
|
+
// Bottom-right corner with padding
|
|
4798
|
+
const sigX = width - sigSize * 2.5;
|
|
4799
|
+
const sigY = height - sigSize * 2.5;
|
|
4800
|
+
const sigSegments = 4 + Math.floor(sigRng() * 4); // 4-7 segments
|
|
4801
|
+
const sigColor = (0, $b5a262d09b87e373$export$f2121afcad3d553f)(colorHierarchy.accent, 0.15);
|
|
4802
|
+
ctx.save();
|
|
4803
|
+
ctx.globalAlpha = 0.12 + sigRng() * 0.08;
|
|
4804
|
+
ctx.translate(sigX, sigY);
|
|
4805
|
+
ctx.strokeStyle = sigColor;
|
|
4806
|
+
ctx.fillStyle = (0, $b5a262d09b87e373$export$f2121afcad3d553f)(colorHierarchy.dominant, 0.06);
|
|
4807
|
+
ctx.lineWidth = Math.max(0.5, 0.8 * scaleFactor);
|
|
4808
|
+
// Outer ring
|
|
4809
|
+
ctx.beginPath();
|
|
4810
|
+
ctx.arc(0, 0, sigSize, 0, Math.PI * 2);
|
|
4811
|
+
ctx.stroke();
|
|
4812
|
+
ctx.fill();
|
|
4813
|
+
// Inner geometric pattern — unique per hash
|
|
4814
|
+
ctx.beginPath();
|
|
4815
|
+
for(let s = 0; s < sigSegments; s++){
|
|
4816
|
+
const angle1 = sigRng() * Math.PI * 2;
|
|
4817
|
+
const angle2 = sigRng() * Math.PI * 2;
|
|
4818
|
+
const r1 = sigSize * (0.2 + sigRng() * 0.6);
|
|
4819
|
+
const r2 = sigSize * (0.2 + sigRng() * 0.6);
|
|
4820
|
+
ctx.moveTo(Math.cos(angle1) * r1, Math.sin(angle1) * r1);
|
|
4821
|
+
ctx.lineTo(Math.cos(angle2) * r2, Math.sin(angle2) * r2);
|
|
4822
|
+
}
|
|
4823
|
+
ctx.stroke();
|
|
4824
|
+
// Center dot
|
|
4825
|
+
ctx.beginPath();
|
|
4826
|
+
ctx.arc(0, 0, sigSize * 0.12, 0, Math.PI * 2);
|
|
4827
|
+
ctx.fillStyle = sigColor;
|
|
4828
|
+
ctx.fill();
|
|
4829
|
+
ctx.restore();
|
|
4830
|
+
}
|
|
4601
4831
|
ctx.globalAlpha = 1;
|
|
4602
4832
|
}
|
|
4603
4833
|
|