git-hash-art 0.2.0 → 0.4.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 ADDED
@@ -0,0 +1,264 @@
1
+ # Art Generation Algorithm
2
+
3
+ This document describes the deterministic art generation pipeline used by `git-hash-art`. Every step derives its randomness from the input hash via a seeded mulberry32 PRNG, guaranteeing identical output for identical input.
4
+
5
+ ## Pipeline Overview
6
+
7
+ ```
8
+ Hash String
9
+
10
+ ├─► Seed (mulberry32 PRNG)
11
+
12
+ ├─► Color Scheme (hash-driven variation + scheme type selection)
13
+
14
+ └─► Rendering Pipeline
15
+
16
+ 1. Background Layer (radial gradient)
17
+ 1b. Layered Background (faint shapes + concentric rings)
18
+ 2. Composition Mode Selection
19
+ 3. Focal Points + Void Zones (negative space)
20
+ 4. Flow Field Initialization
21
+ 5. Shape Layers (× N layers)
22
+ │ ├─ Blend Mode (per-layer compositing)
23
+ │ ├─ Render Style (fill+stroke, wireframe, dashed, watercolor, etc.)
24
+ │ ├─ Position (composition mode + focal bias + density check)
25
+ │ ├─ Shape Selection (layer-weighted)
26
+ │ ├─ Atmospheric Depth (desaturation on later layers)
27
+ │ ├─ Styling (transparency, glow, gradients, color jitter)
28
+ │ ├─ Organic Edges (~15% watercolor bleed)
29
+ │ └─ Recursive Nesting (~15% of large shapes)
30
+ 6. Flow-Line Pass (tapered brush strokes)
31
+ 7. Noise Texture Overlay
32
+ 8. Organic Connecting Curves
33
+ ```
34
+
35
+ ## 1. Deterministic RNG
36
+
37
+ All randomness flows from a single **mulberry32** PRNG seeded by hashing the full input string:
38
+
39
+ ```
40
+ seed = hash(gitHash) → mulberry32 state
41
+ rng() → float in [0, 1)
42
+ ```
43
+
44
+ The old approach extracted 2-char hex pairs from the hash (only ~20 unique values in a 40-char hash). Mulberry32 produces a full 32-bit uniform stream from any seed, eliminating correlation artifacts.
45
+
46
+ ## 2. Color Scheme
47
+
48
+ The `SacredColorScheme` class derives three harmonious palettes from the hash:
49
+
50
+ | Palette | Method | Purpose |
51
+ |---------|--------|---------|
52
+ | Base | `color-scheme` lib, hue = seed % 360, hash-driven scheme type | Primary shape colors |
53
+ | Complementary | hue = seed + 180°, contrasting variation | Contrast accents |
54
+ | Triadic | hue = seed + 120° | Additional variety |
55
+
56
+ These are merged and deduplicated into a single 6-8 color palette. Background colors are darkened variants (65% and 55% brightness) of the base scheme.
57
+
58
+ ### Hash-Driven Color Variation
59
+
60
+ The hash deterministically selects both a **scheme type** and a **color variation**, producing dramatically different palettes from the same hue:
61
+
62
+ | Variation | Character |
63
+ |-----------|-----------|
64
+ | `soft` | Muted, gentle tones |
65
+ | `hard` | High saturation, vivid |
66
+ | `pastel` | Light, airy |
67
+ | `light` | Bright, open |
68
+ | `dark` | Deep, moody |
69
+ | `default` | Balanced, neutral |
70
+
71
+ Scheme types also vary: `analogic`, `mono`, `contrast`, `triade`, `tetrade`. The complementary palette uses a contrasting variation (e.g., if base is `soft`, complementary uses `hard`) to create intentional color tension.
72
+
73
+ ### Color Utilities
74
+
75
+ - **`hexWithAlpha(hex, alpha)`** — converts hex to `rgba()` for transparency
76
+ - **`jitterColor(hex, rng, amount)`** — applies ±amount RGB jitter per channel for organic variation
77
+ - **`desaturate(hex, amount)`** — blends toward luminance gray for atmospheric depth
78
+ - **Positional blending** — shape fill color is biased by canvas position, creating smooth color flow across the image
79
+
80
+ ## 3. Background
81
+
82
+ A radial gradient fills the canvas from center to corners using two darkened base-scheme colors. This creates depth before any shapes are drawn.
83
+
84
+ ### Layered Background
85
+
86
+ After the gradient, a second pass adds visual texture to the background:
87
+
88
+ - **Faint shapes** — 3-7 large, very low-opacity circles (3-8% alpha) drawn with `soft-light` blending, creating subtle color pools
89
+ - **Concentric rings** — 2-4 rings emanating from center at ~2-5% opacity, adding structure without competing with foreground shapes
90
+
91
+ This prevents the background from feeling flat and gives the image depth before the main shape layers begin.
92
+
93
+ ## 4. Composition Modes
94
+
95
+ The hash deterministically selects one of five composition strategies that control how shapes are positioned on the canvas:
96
+
97
+ | Mode | Description |
98
+ |------|-------------|
99
+ | **Radial** | Shapes emanate from the center with distance following a power curve (denser near center) |
100
+ | **Flow-field** | Random positions; shapes are rotated to align with a hash-derived vector field |
101
+ | **Spiral** | Shapes follow a multi-turn spiral path outward from center with slight scatter |
102
+ | **Grid-subdivision** | Canvas is divided into cells; shapes are placed randomly within cells |
103
+ | **Clustered** | 3-5 cluster centers are generated; shapes scatter around the nearest cluster |
104
+
105
+ Each mode produces fundamentally different visual character from the same shape set.
106
+
107
+ ## 5. Focal Points & Negative Space
108
+
109
+ 1-2 focal points are placed on the canvas (kept away from edges). Every shape position is pulled toward the nearest focal point by a strength factor (30-70%), creating areas of visual density and intentional-looking composition rather than uniform scatter.
110
+
111
+ ### Void Zones (Negative Space)
112
+
113
+ 1-2 void zones are generated at random positions. Shapes that land inside a void zone have an 85% chance of being skipped, creating deliberate areas of breathing room. A few shapes bleed through (15%) to keep the edges organic rather than hard-cut.
114
+
115
+ ### Density Awareness
116
+
117
+ Before placing each shape, the renderer checks how many shapes already exist nearby. If local density exceeds ~15% of the per-layer shape count, there's a 60% chance the shape is skipped. This prevents areas from becoming an opaque blob and creates natural visual rhythm.
118
+
119
+ ## 6. Shape Layers
120
+
121
+ The image is built in N layers (default: 4). Each layer has its own characteristics:
122
+
123
+ ### Layer Properties
124
+
125
+ | Property | Behavior |
126
+ |----------|----------|
127
+ | Opacity | Decays gently per layer (0.7 → 0.58 → 0.46 → 0.34), minimum 0.15 |
128
+ | Size scale | Later layers use progressively smaller shapes (×0.85, ×0.70, ×0.55) |
129
+ | Shape weights | Early layers favor basic shapes; later layers favor complex/sacred |
130
+ | Per-shape opacity | Additional random jitter (50-100% of layer opacity) |
131
+ | Blend mode | Each layer gets a hash-derived `globalCompositeOperation` (see below) |
132
+ | Render style | Each layer has a dominant render style; 30% of shapes pick their own |
133
+ | Atmospheric depth | Later layers desaturate colors by up to 30%, simulating distance |
134
+
135
+ ### Blend Modes (Per-Layer Compositing)
136
+
137
+ Each layer deterministically selects a `globalCompositeOperation` from: `source-over`, `screen`, `multiply`, `overlay`, `soft-light`, `color-dodge`, `color-burn`, `lighter`. There's a 40% chance of `source-over` (default) to keep some images clean, while the other modes create rich color interactions where shapes overlap — the kind of depth that makes output feel painterly rather than stacked.
138
+
139
+ ### Render Styles (Per-Shape Treatment)
140
+
141
+ Instead of always `fill()` + `stroke()`, each shape gets a rendering treatment:
142
+
143
+ | Style | Description | Probability |
144
+ |-------|-------------|-------------|
145
+ | `fill-and-stroke` | Classic solid fill with outline | ~29% (weighted) |
146
+ | `fill-only` | Soft shapes with no outline | ~14% |
147
+ | `stroke-only` | Wireframe with ghost fill at 30% alpha | ~14% |
148
+ | `double-stroke` | Outer stroke at 2× width + inner stroke in fill color | ~14% |
149
+ | `dashed` | Dashed outline (5% size dash, 3% gap) | ~14% |
150
+ | `watercolor` | 3-4 slightly offset passes at low opacity for bleed effect | ~14% |
151
+
152
+ 70% of shapes in a layer use the layer's dominant style; 30% pick independently. Additionally, ~15% of `fill-and-stroke` shapes are upgraded to `watercolor` for organic edge effects.
153
+
154
+ ### Atmospheric Depth (Per-Layer Desaturation)
155
+
156
+ Later layers progressively desaturate their colors (0% on layer 0, up to 30% on the final layer). This simulates atmospheric perspective — distant shapes appear more muted, creating a sense of foreground/background depth.
157
+
158
+ ### Shape Selection (Layer-Weighted)
159
+
160
+ Shapes are divided into three categories with weights that shift across layers:
161
+
162
+ | Category | Shapes | Early layers | Late layers |
163
+ |----------|--------|-------------|-------------|
164
+ | **Basic** | circle, square, triangle, hexagon, diamond, cube | High weight | Low weight |
165
+ | **Complex** | star, platonic solid, fibonacci spiral, islamic pattern, celtic knot, merkaba, fractal | Medium | Medium-high |
166
+ | **Sacred** | mandala, flower of life, tree of life, Metatron's cube, Sri Yantra, seed of life, vesica piscis, torus, egg of life | Low | High |
167
+
168
+ ### Size Distribution
169
+
170
+ Shape sizes follow a **power distribution** (`Math.pow(rng(), 1.8)`) — producing many small shapes and few large ones, which creates natural visual hierarchy.
171
+
172
+ ### Styling Per Shape
173
+
174
+ Each shape receives:
175
+
176
+ - **Semi-transparent fill** — alpha between 0.2-0.7, creating watercolor-style blending where shapes overlap
177
+ - **Color jitter** — ±8% RGB variation on fills, ±5% on strokes, so no two shapes using the "same" palette color are pixel-identical
178
+ - **Positional color** — fill color is biased by the shape's canvas position, creating smooth color flow
179
+ - **Glow effect** — 45% of sacred shapes and 20% of others get a `shadowBlur` glow (8-28px scaled), with glow color at 60% opacity
180
+ - **Gradient fill** — ~30% of shapes get a radial gradient between two jittered palette colors instead of a flat fill
181
+ - **Variable stroke width** — 0.5-2.5px scaled to canvas size
182
+
183
+ ### Recursive Nesting
184
+
185
+ ~15% of shapes larger than 40% of max size spawn 1-3 inner shapes:
186
+ - Inner shapes are drawn at the parent's position with small random offsets
187
+ - They use more complex/sacred shape types (layer ratio biased +0.3)
188
+ - Sized at 15-40% of the parent
189
+ - More transparent than the parent layer
190
+
191
+ ## 7. Flow-Line Pass (Tapered Brush Strokes)
192
+
193
+ 6-16 flowing curves are drawn across the canvas, following the hash-derived vector field:
194
+
195
+ - Each line starts at a random position and takes 30-70 steps
196
+ - At each step, direction is determined by the flow field angle at that position plus slight random wobble
197
+ - Lines stop if they leave the canvas bounds
198
+ - **Tapered width** — each line starts at 1-4px and tapers to 20% of its starting width by the end, simulating a brush stroke that lifts off the canvas
199
+ - **Tapered opacity** — alpha also decays along the stroke, creating natural fade-out
200
+ - Individual segments are drawn with `lineCap: "round"` for smooth joins
201
+
202
+ The flow field is defined by:
203
+ ```
204
+ angle(x, y) = baseAngle + sin(x/w × freq × 2π) × π/2 + cos(y/h × freq × 2π) × π/2
205
+ ```
206
+
207
+ ## 8. Noise Texture Overlay
208
+
209
+ A dedicated noise RNG (seeded separately from the main RNG to avoid affecting shape generation) renders thousands of 1px dots across the canvas:
210
+
211
+ - Density: ~1 dot per 800 square pixels
212
+ - Each dot is either black or white (50/50)
213
+ - Very low opacity (1-4%)
214
+ - Creates subtle film-grain texture that adds organic depth
215
+
216
+ ## 9. Organic Connecting Curves
217
+
218
+ Quadratic bezier curves connect nearby shapes:
219
+
220
+ - Number of curves scales with canvas area (~8 per megapixel)
221
+ - Each curve connects two shapes that were drawn near each other in sequence
222
+ - Control points are offset perpendicular to the connecting line with random bulge
223
+ - Drawn at low opacity (6-16%) with palette colors at 30% alpha
224
+
225
+ ## Shape Implementations
226
+
227
+ ### Basic Shapes
228
+ Standard geometric primitives drawn as canvas paths (beginPath → moveTo/lineTo/arc → closePath). The draw pipeline calls `fill()` and `stroke()` after the path is defined.
229
+
230
+ ### Complex Shapes
231
+ More intricate geometry including:
232
+ - **Platonic solids** — 2D projections with all edges drawn between vertices
233
+ - **Fibonacci spiral** — iterative arc segments following the golden ratio
234
+ - **Islamic pattern** — 8-pointed star grid at intersections
235
+ - **Celtic knot** — bezier over/under weaving pattern
236
+ - **Mandala** — concentric circles with radial lines
237
+ - **Fractal tree** — recursive branching at ±30° with 0.7× length decay
238
+
239
+ ### Sacred Geometry
240
+ Mathematically precise sacred geometry patterns:
241
+ - **Flower of Life** — 7 overlapping circles in hexagonal arrangement
242
+ - **Tree of Life** — 10 Sephirot nodes with connecting paths
243
+ - **Metatron's Cube** — 13 vertices (center + inner/outer hexagons) fully connected
244
+ - **Sri Yantra** — 9 interlocking triangles at two radii
245
+ - **Seed of Life** — 7 circles (same as Flower of Life, different scale)
246
+ - **Vesica Piscis** — two overlapping circles
247
+ - **Torus** — 2D projection of a torus via line segments
248
+ - **Egg of Life** — 7 circles in tight hexagonal packing
249
+
250
+ ## Configuration
251
+
252
+ All parameters are exposed via `GenerationConfig`:
253
+
254
+ | Parameter | Default | Effect |
255
+ |-----------|---------|--------|
256
+ | `width` | 2048 | Canvas width in pixels |
257
+ | `height` | 2048 | Canvas height in pixels |
258
+ | `gridSize` | 5 | Base shape count = gridSize² × 1.5 |
259
+ | `layers` | 4 | Number of rendering layers |
260
+ | `minShapeSize` | 30 | Minimum shape size (scaled to canvas) |
261
+ | `maxShapeSize` | 400 | Maximum shape size (scaled to canvas) |
262
+ | `baseOpacity` | 0.7 | First layer opacity |
263
+ | `opacityReduction` | 0.12 | Opacity decay per layer |
264
+ | `shapesPerLayer` | auto | Override auto-calculated shape count |
package/CHANGELOG.md CHANGED
@@ -4,12 +4,27 @@ 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.4.0](https://github.com/gfargo/git-hash-art/compare/0.3.0...0.4.0)
8
+
9
+ - feat: artistic enhancements for richer generative output [`#10`](https://github.com/gfargo/git-hash-art/pull/10)
10
+
11
+ #### [0.3.0](https://github.com/gfargo/git-hash-art/compare/0.2.0...0.3.0)
12
+
13
+ > 19 March 2026
14
+
15
+ - feat: evolve art generation with composition modes, flow fields, nesting, and texture [`#9`](https://github.com/gfargo/git-hash-art/pull/9)
16
+ - chore: release v0.3.0 [`ccceb41`](https://github.com/gfargo/git-hash-art/commit/ccceb41e8eec92fc1b7c857cfaa4ddb255b098e5)
17
+ - refactor(browser): simplify export statement [`dc193c9`](https://github.com/gfargo/git-hash-art/commit/dc193c972511c5b0eb9e6a781c423aa005453007)
18
+
7
19
  #### [0.2.0](https://github.com/gfargo/git-hash-art/compare/0.1.0...0.2.0)
8
20
 
21
+ > 19 March 2026
22
+
9
23
  - feat: add cross-platform browser support [`#8`](https://github.com/gfargo/git-hash-art/pull/8)
10
24
  - feat: add visual effects for richer art generation [`#6`](https://github.com/gfargo/git-hash-art/pull/6)
11
25
  - fix: improve art generation quality [`#5`](https://github.com/gfargo/git-hash-art/pull/5)
12
26
  - docs: update README to reflect current algorithm and features [`15879c6`](https://github.com/gfargo/git-hash-art/commit/15879c62480a2d418c4ef8dcb479d912a7160345)
27
+ - chore: release v0.2.0 [`6867e06`](https://github.com/gfargo/git-hash-art/commit/6867e067f6dda0f4a4fd444b412772ddecb01555)
13
28
  - fix(shapes): handle missing shape type default [`860bfd3`](https://github.com/gfargo/git-hash-art/commit/860bfd36caab97d6ed44dd441ea1a995863b6068)
14
29
 
15
30
  #### [0.1.0](https://github.com/gfargo/git-hash-art/compare/0.0.4...0.1.0)