After completing the mosaic and scattered-collage stages of my WordPress Background Studio, I began asking a different question: could the system choose a mathematical composition method as well as choosing media? That question changed the project’s category. What began as background randomization became a modular browser-based generative-art engine with twelve algorithms, deterministic seeds, record-aware identity and a deployment process designed for a live, resource-constrained server.
I described the earlier evolution from a repeating background to mosaics and random collage in Incremental Development of a WordPress GIF Mosaic Background Engine. This article begins where that one stops. I will not repeat the complete history of Static, Random, Mosaic, Separate Panels, density controls and gap colours. Instead, I want to explain the architectural turn that made mathematical generators possible, the failures that corrected my assumptions, and the evidence that established the final production state.
The point at which a layout became an engine
The previous system could already choose eligible media records, apply equal or weighted probability, preserve a choice for a page, session or day, and render several records in fixed or scattered arrangements. Its random collage was advanced, but the geometry still belonged to particular renderers. A mosaic script knew how to make a mosaic; a scattered-panel script knew how to scatter panels. Adding another visual structure meant extending another specialized branch.
I wanted both automatic and deliberate control. In automatic mode, the system should select a specific algorithm and show its real name. In manual mode, I should be able to choose Hilbert, Voronoi, Phyllotaxis or another method directly. The word “Automatic” describes a setting, not what appears on screen, so printing it in the public badge would have been rather like a museum label saying “Painting selected by database.” Technically true, visually unhelpful.
The production environment remained intentionally modest: Debian 13 with Linux kernel 6.12.101, approximately one virtual CPU and 1 GB of memory, PHP 8.4.24 with OPcache, WordPress 7.0.4, and a heavily customized Penscratch 1.0.3 theme. Node.js 20.19.2 provided JavaScript syntax validation. By this stage, the live Background Studio contained 13 records.
This last number matters. The engine did not need twelve algorithms because it had twelve records, and it did not need to generate new images. It needed to produce different spatial relationships among the existing records. The media library would provide the vocabulary; the algorithms would provide the grammar.
Constraints that shaped the design
The first constraint was preservation. Static, Random, Mosaic, Random + Mosaic, video handling and earlier collage modes already worked. Generative mode had to join them without rewriting their saved settings or quietly changing the existing WordPress option. Every installation therefore treated the option data as protected state and compared its checksum before and after deployment.
The second constraint concerned identity. Two deliberate Hilbert records pointed to the same WebP URL but used different configured dimensions. Those dimensions produced visibly different patterns. A conventional deduplication routine might see one URL twice and remove a “duplicate”; in this project, that would destroy an intentional visual distinction.
A Background Studio record is a visual instrument. Its URL identifies an asset, but it does not completely identify the instrument.
The engine consequently uses the record ID as its preferred identity and includes dimensions and other visual settings in a configuration signature. URL fallback is allowed only when one record matches unambiguously. This decision came from concrete evidence in the live library, not from an abstract preference for elaborate identifiers.
Other constraints followed from the earlier system:
- The composition had to remain fixed behind the page and outside document flow.
- Decorative regions could not intercept pointer or keyboard interaction.
- The renderer had to preserve each record’s colour, size, repetition, position and attachment settings.
- Reduced-motion preferences had to prevent animated generative rendering.
- Fixed seeds had to reproduce a composition.
- Session and daily persistence had to remain available.
- Automatic complexity needed a bounded range of 1–5.
- The public badge could display the actual algorithm, but not internal complexity codes.
- New geometry libraries should be added only when they solved a real mathematical problem better than small local code.
- Every production modification needed a checkpoint and a validated candidate.
These constraints made the design less “free,” but that was useful. Generative systems often become more interesting when freedom operates within intelligible boundaries. Unconstrained randomness is easy; meaningful variation requires a structure that can say no occasionally.
A small generator interface
The central architectural move was to extract geometry behind a registry. A generator accepts a context and returns normalized regions. It does not select media, write WordPress options or know how the page is styled.
The core interface is deliberately small:
var generators = Object.create(null);
function registerGenerator(name, generator) {
var key = String(name || '')
.trim()
.toLowerCase();
if (
!/^[a-z][a-z0-9_-]*$/.test(key) ||
!generator ||
typeof generator.generate !== 'function'
) {
throw new TypeError(
'Invalid generator registration'
);
}
generators[key] = generator;
}
function generate(name, context) {
var key = String(name || '')
.trim()
.toLowerCase();
if (!generators[key]) {
throw new Error(
'Unknown generator: ' + key
);
}
return generators[key].generate(context || {});
}
Every returned region uses the same normalized coordinate model:
{
x: 12.5,
y: 0,
width: 25,
height: 33.33333,
clipPath: 'polygon(0 0,100% 0,0 100%)'
}
The optional clipPath allows triangles and arbitrary polygons to use the same renderer as rectangles. Coordinates are percentages from 0 to 100, which keeps the algorithms independent of the visitor’s physical viewport size.
The renderer performs the common work: it chooses eligible records, requests geometry, creates one decorative tile per region, applies a record’s media configuration and attaches identity metadata. A simplified section looks like this:
regions.forEach(function (region, index) {
var item = palette[index % palette.length];
var tile = document.createElement('div');
tile.className =
'yin-background-generative-tile';
tile.style.left = region.x + '%';
tile.style.top = region.y + '%';
tile.style.width = region.width + '%';
tile.style.height = region.height + '%';
if (region.clipPath) {
tile.style.clipPath = region.clipPath;
tile.style.webkitClipPath = region.clipPath;
}
applyInstrument(tile, item);
layer.appendChild(tile);
});
This separation produced an important practical benefit. Adding Ulam Spiral no longer required creating another complete background renderer. I only needed a function that produced valid regions, an administration option, an allowlisted identifier and a badge label.
Configuration identity beyond the URL
The configuration signature records the properties that can materially change the appearance of one instrument. A simplified version is:
function configurationSignature(item) {
item = item || {};
return JSON.stringify([
'instrument-v1',
String(item.id || ''),
normalizeUrl(item.url),
String(item.size_mode || 'auto'),
String(item.width || 'auto'),
String(item.height || 'auto'),
String(item.repeat || 'repeat'),
String(item.position_x || 'left'),
String(item.position_y || 'top'),
String(item.attachment || 'scroll'),
String(item.color || '#000000').toLowerCase(),
Math.max(0.01, Number(item.weight) || 1)
]);
}
Including width and height is what preserves the two intentional Hilbert variants. It also means that changing a dimension changes the composition signature and invalidates stale persistence. That behaviour is desirable: a saved composition based on an old visual configuration is no longer the same composition.
Reproducible randomness
The engine uses a deterministic pseudorandom number generator derived from a hashed seed. This is visual randomness, not cryptographic randomness. The distinction matters: the purpose is repeatability, not secrecy.
Separate derived seeds control record selection, geometry, palette order and automatic complexity. A change in one concern therefore does not necessarily scramble every other concern.
var complexity = config.complexityRandom
? 1 + Math.floor(
engine.createRandom(
seed + ':complexity'
)() * 5
)
: configuredComplexity;
var selectionRandom = engine.createRandom(
seed + ':selection'
);
var geometryRandom = engine.createRandom(
seed + ':geometry'
);
var paletteRandom = engine.createRandom(
seed + ':palette'
);
A fixed seed reproduces the result. Page persistence generates a new page seed; session persistence stores a compatible seed in sessionStorage; daily persistence derives a date-based value. Storage failures are caught so that the background can continue without persistence.
Which mathematics to write and which library to use
I did not want to reinvent a well-tested computational geometry library merely to claim that every line was original. Equally, importing a large framework for a short recurrence relation would have made the system heavier without making it clearer.
D3 Delaunay 6.0.4 became the one external mathematical dependency. It provides robust Delaunay triangulation and Voronoi construction, and it also supports the repeated Voronoi calculations required by Lloyd relaxation. The minified browser file and its license are stored locally in the theme, so production does not depend on a third-party CDN.
The other generators were compact enough to implement directly. Their core operations—recursive splitting, grid traversal, golden-angle placement and treemap row construction—are understandable, deterministic and easy to test in isolation.
| Algorithm | Main geometry | Implementation choice |
|---|---|---|
| BSP / Mondrian | Recursive binary partition of the largest rectangle | Small custom generator |
| Adaptive Quadtree | Four-way recursive subdivision | Small custom generator |
| Hilbert | Square cells ordered by a Hilbert curve | Custom index-to-coordinate routine |
| Golden Spiral | Golden-ratio recursive cuts with rotating direction | Small custom generator |
| Voronoi | Cells around seeded sites | D3 Delaunay |
| Delaunay | Triangles connecting seeded sites | D3 Delaunay |
| Lloyd Voronoi | Voronoi sites repeatedly moved toward cell centroids | D3 Delaunay plus custom iteration |
| Phyllotaxis | Golden-angle radial point distribution followed by cells | Custom placement plus D3 Voronoi |
| Radial Fan | Triangular sectors between a seeded centre and perimeter | Small custom generator |
| Squarified Treemap | Area-weighted rectangular rows with controlled aspect ratios | Custom squarify routine |
| Diagonal Truchet | Seeded diagonal subdivisions of a square grid | Small custom generator |
| Ulam Spiral | Square grid traversed from the centre in an outward spiral | Custom directional traversal |
The Ulam traversal, for example, needs only four directions and an increasing step length:
var directions = [
[1, 0],
[0, -1],
[-1, 0],
[0, 1]
];
var stepLength = 1;
var direction = 0;
while (regions.length < grid * grid) {
for (var repeat = 0; repeat < 2; repeat += 1) {
var vector = directions[direction % 4];
for (var step = 0; step < stepLength; step += 1) {
x += vector[0];
y += vector[1];
addRegion();
}
direction += 1;
}
stepLength += 1;
}
This code does not attempt to generate prime-number visualizations. It uses the square-spiral ordering as a spatial composition method. Calling it an Ulam-style spiral is therefore precise; claiming that it performs number-theoretical analysis would be an invention.
Five phases instead of one reconstruction
The Generative Engine was installed in five bounded phases. This was not the shortest possible route, but it kept every intermediate state understandable and recoverable. By the end, I had accumulated backup archives with something approaching liturgical regularity. In production administration, repetition can be a virtue.
Phase 1 established the reusable engine, deterministic PRNG, record identity, configuration signatures and the first BSP generator. The existing mosaic and collage scripts could begin consuming engine services without changing their public behaviour. This was where the shared-URL Hilbert evidence changed the identity model: deduplication had to operate on configured records, not assets alone.
Phase 2 added Generative as a real Background Studio display mode, created the common renderer, introduced Quadtree and Hilbert, and added administration controls for algorithm, complexity, instrument range and seed. The mode could select a generator automatically or use one selected manually.
A separate extension then introduced the bottom-right brutalist badge. Its public contract is deliberately narrow:
BG ENGINE ACTUAL ALGORITHM NAME
The badge never shows AUTOMATIC. It also no longer shows C1, C2 or another complexity code. Complexity is useful diagnostic state, but it is not the visitor-facing identity of the composition.
Phase 3 added Golden Spiral, Voronoi and Delaunay. This phase introduced the local D3 Delaunay dependency. Automatic permutation expanded from three to six algorithms.
Phase 4 added Lloyd Voronoi, Phyllotaxis and Radial Fan, increasing the catalogue to nine. These algorithms were related to Phase 3 geometrically, but their visual behaviour was sufficiently different to deserve independent names and controls.
Phase 5 added Squarified Treemap, Diagonal Truchet and Ulam Spiral. These widened the vocabulary beyond point-based computational geometry: one organizes weighted areas, one creates combinatorial triangular tiling, and one uses an ordered square-grid traversal. Automatic permutation now selects from all twelve.
PHP remains authoritative for allowed generator identifiers. A malformed or unknown value returns the previously valid settings instead of quietly saving an unusable state.
<?php
$allowed_generators = array(
'random',
'bsp',
'quadtree',
'hilbert',
'golden-spiral',
'voronoi',
'delaunay',
'lloyd-voronoi',
'phyllotaxis',
'radial-fan',
'treemap',
'truchet',
'ulam-spiral',
);
if (
! in_array(
$generator,
$allowed_generators,
true
)
) {
add_settings_error(
YIN_BACKGROUND_STUDIO_OPTION,
'invalid_generative_generator',
'Invalid Generative algorithm. Nothing was saved.',
'error'
);
return $previous;
}
The front-end enqueue chain mirrors the phase dependency chain. D3 loads before Phase 3; Phase 3 loads before Phase 4; Phase 4 loads before Phase 5; the common renderer loads last. WordPress file modification times provide asset versions, which reduces stale browser caching after an update.
Deployment as a chain of evidence
I was applying these changes directly to a live customized theme, so “the code looks reasonable” was never an adequate deployment test. AI generated much of the candidate code and patch structure, but deterministic tools decided whether that code could move forward.
The procedure separated several kinds of validation that are easy to blur together:
- Syntax validation asked whether PHP and JavaScript could be parsed.
- Structural validation confirmed expected files, markers, loader relationships and exact patch targets.
- Candidate validation applied patches to an isolated copy and checked resulting hashes before production installation.
- Semantic geometry testing executed generators and checked region counts, bounds, coverage and determinism.
- Regression validation confirmed that saved Background Studio settings had not changed.
- Deployment validation checked the installed live files and bootstrapped WordPress.
- Runtime validation inspected the actual browser state and public badge.
A condensed version of the candidate process is:
set -euo pipefail
WP_ROOT="/var/www/example-site"
THEME="$WP_ROOT/wp-content/themes/penscratch"
CANDIDATE="$(mktemp -d /tmp/generative-candidate.XXXXXX)"
PATCH_FILE="$CANDIDATE/integration.patch"
mkdir -p \
"$CANDIDATE/theme/inc" \
"$CANDIDATE/theme/js" \
"$CANDIDATE/theme/css"
cp "$THEME/inc/background-generative-extension.php" \
"$CANDIDATE/theme/inc/"
cp "$THEME/js/background-generative-mode.js" \
"$CANDIDATE/theme/js/"
patch \
--dry-run \
--fuzz=0 \
-p1 \
-d "$CANDIDATE/theme" \
< "$PATCH_FILE"
patch \
--fuzz=0 \
-p1 \
-d "$CANDIDATE/theme" \
< "$PATCH_FILE"
php -l \
"$CANDIDATE/theme/inc/background-generative-extension.php"
node --check \
"$CANDIDATE/theme/js/background-generative-mode.js"
sha256sum \
"$CANDIDATE/theme/inc/background-generative-extension.php" \
"$CANDIDATE/theme/js/background-generative-mode.js"
The live settings checksum was captured separately:
SETTINGS_HASH="$(
php -r '
define("WP_USE_THEMES", false);
require $argv[1] . "/wp-load.php";
echo hash(
"sha256",
serialize(
get_option(
"yin_background_studio",
array()
)
)
);
' "$WP_ROOT"
)"
The same calculation ran after installation. A mismatch would stop the procedure because code deployment had no authority to alter the saved studio configuration.
Every material phase also created a timestamped archive before changing live files. An opaque compressed one-liner might have been shorter to transmit, but I rejected the initial Base64-and-gzip form. If I am about to run something as root, being able to read it is part of the interface, not an optional decoration.
Failures that changed the model
The most useful failures did more than identify a bad line. They changed how I understood the system or how the next installer was designed.
The server did not have patch
The first multi-file installer stopped with:
bash: line 941: patch: command not found
Nothing had been installed. I considered using Python replacement because earlier deployments had used it successfully, but a unified patch remained the better tool for a known multi-file change: it could perform a dry run, report every hunk and apply the same reviewed diff to an isolated candidate.
GNU patch 2.8 was installed. The small VPS had apparently interpreted “minimal server” as a package-selection philosophy.
A malformed patch was not a code failure
The next attempt parsed several files successfully and then stopped:
patch: **** malformed patch at line 692
The important evidence was the word malformed. JavaScript execution had not failed; the patch parser could not understand the diff structure. The corrected patch was regenerated and dry-run before use. Once fixed, all hunks applied, some with harmless one-line offsets caused by the exact live source.
This distinction prevented the wrong diagnosis. Rewriting a valid generator would not repair a malformed diff.
The Phase 2 loader missed its expected line
The first activation patch for functions.php reported:
Hunk #1 FAILED at 750. 1 out of 1 hunk FAILED
The four Phase 2 files had already passed PHP or JavaScript syntax checks, but the loader had not been installed. This time, exact line-oriented patching was less suitable. A corrected activation built a candidate from the real functions.php, found a verified source anchor, inserted the loader once, checked the replacement count, validated PHP, bootstrapped WordPress and then installed the candidate.
That experience settled the patch-versus-Python question for me. A unified patch is excellent when the surrounding source is known and several files must change together. A small Python transformation is safer when one insertion must be located semantically and its occurrence count can be asserted. Tools do not need loyalty; they need appropriate jobs.
Node.js judged the file by its extension
The badge administration script failed validation with:
TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".Scu75glQ2E"
The candidate was a JavaScript file stored under a generic mktemp name. Node.js 20.19.2 was being asked to check it as a module but could not infer the format. The fix was small:
TEMP="$(
mktemp \
--suffix=.js \
/tmp/generative-admin.XXXXXX
)"
The source then passed unchanged. Node had, quite literally, judged the script by its cover.
npm was absent, but the application did not need npm
The first Phase 3 dependency installer stopped with:
STOP: npm is not installed.
Installing an entire package-management environment on the production VPS would have solved the installer’s assumption, but it was unnecessary for the application. The actual requirement was one known browser distribution file and its license.
The corrected procedure downloaded the official D3 Delaunay 6.0.4 package archive, verified it, extracted d3-delaunay.min.js and LICENSE, and stored them under js/vendor/. Installing npm for this would have resembled building a supermarket to obtain one apple.
A checksum disagreement did not prove broken geometry
The first Phase 4 source step stopped because the pasted file’s SHA-256 did not equal the expected textual checksum. Node syntax validation passed, and the difference could have been formatting. The earlier gate therefore answered the wrong question too rigidly: “Are these bytes identical?” when the new source first needed to establish “Does this generator behave correctly?”
The revised step retained syntax validation and added semantic tests. It generated Lloyd Voronoi, Phyllotaxis and Radial Fan regions, checked their counts and verified that coordinates were finite and bounded. All three passed. Checksums remained valuable for integrating known existing files; they were no longer treated as a substitute for behavioural evidence about newly pasted source.
Noisy terminal text versus authoritative results
Some long heredoc pastes produced visually garbled fragments around the final echo commands. The reliable evidence was elsewhere: patch output, parser results, WordPress bootstrap messages, settings hashes and the child exit code. This was a useful human–computer interaction lesson. A terminal can display an untidy transcript while still executing a well-delimited heredoc correctly; one should inspect the authoritative signals before inventing a new failure.
An installer that exits with status 1 before changing production is not wasted work. In this project, exit status 1 was occasionally the most honest collaborator in the room.
Automatic complexity and a badge that tells the truth
The administration interface initially offered a fixed complexity from 1 to 5. I later added an Automatic complexity (1–5) checkbox. When it is enabled, the selected level is derived from the same composition seed family, so a fixed seed still reproduces both geometry and complexity.
Complexity does not mean exactly the same thing for every generator. For Hilbert it maps to curve order. For Voronoi-related algorithms it affects site count and, for Lloyd Voronoi, relaxation iterations. For Truchet it determines grid size. Ulam uses odd grids, reaching 9 × 9 at maximum complexity. This is a shared artistic scale, not a claim that 25 Voronoi cells are mathematically equivalent to Hilbert order 3.
The badge originally experimented with output such as VORONOI / C4. I decided that this exposed implementation detail without helping the visual reading of the page. The final output uses the shorter system label BG ENGINE and the exact algorithm beneath it.
The badge waits for window.YinBackgroundGenerativeState, normalizes the generator identifier and mounts only after it can name a concrete result. Consequently, automatic selection still produces VORONOI, DIAGONAL TRUCHET or another exact name. “Automatic” never escapes from the administration setting into the homepage.
What the tests actually established
Geometry tests were deliberately specific. A function returning an array was insufficient; a plausible array can still contain negative widths, duplicated positions or incomplete coverage.
The verified generator results included:
GOLDEN-SPIRAL PASS: regions=9 VORONOI PASS: regions=18 DELAUNAY PASS: regions=24 LLOYD-VORONOI PASS: regions=20 PHYLLOTAXIS PASS: regions=24 RADIAL-FAN PASS: regions=20 TREEMAP PASS: regions=17 TRUCHET PASS: regions=50 ULAM-SPIRAL PASS: regions=49
Phase 5 testing also confirmed that every coordinate was finite, every bounding box remained within the normalized viewport, the 17-region treemap covered an area of approximately 10,000 normalized square units, a Truchet grid of 5 produced 50 triangles, and an Ulam grid of 7 produced 49 unique cells.
The final server validation reported:
WordPress bootstrap PASS Display mode: generative Configured generator: random Phase 5 integration: ACTIVE FINAL SERVER CHECK PASS Mode version: 5.0.0 Algorithms: 12
A browser test then established the real operational state:
{
modeVersion: '5.0.0',
actualAlgorithm: 'ulam-spiral',
availableAlgorithms: 12,
actualComplexity: 1,
automaticComplexity: true,
badge: 'BG ENGINE ULAM SPIRAL'
}
This result matters more than a successful syntax check. It confirms that the dependency chain loaded, the common renderer registered Quadtree and Hilbert, automatic selection chose a specific generator, automatic complexity produced a bounded value, the runtime state was published and the badge read that state correctly.
The WordPress option checksum remained unchanged during installation. The 13 records were still available, and the two same-URL Hilbert configurations retained their distinct identities.
Performance, accessibility and remaining limits
Generative geometry runs in the browser. The small VPS serves JavaScript, CSS, settings and original media; it does not rasterize mosaics or generate composite image files. This keeps server memory usage modest, but it does not make client-side rendering free.
At maximum complexity, Ulam can create 81 square regions. Truchet can create 72 triangular elements from a 6 × 6 grid. Reusing one media URL normally benefits from browser caching, yet the browser must still composite every visible region. Several animated GIFs can therefore affect battery life and painting performance even when network transfer is efficient.
The common layer uses fixed positioning, pointer-events: none, user-select: none, aria-hidden="true" and containment. The foreground page remains in a higher stacking context. Regions do not participate in document flow, so the background does not create cumulative layout shift.
Generative rendering currently exits when prefers-reduced-motion: reduce is active. That is a conservative behaviour. A future version could offer a specifically verified static generative fallback, but it should not assume that every WebP, GIF or video is motion-safe.
The public design also depends on foreground opacity and contrast. A mathematically elegant background can still be a bad reading surface. Geometry is not absolution; the article content must remain legible.
Other limitations remain clear:
- Visual tests cannot be replaced entirely by geometry tests, especially on mobile viewports.
- Direct theme customizations may be overwritten if the customized theme is replaced without merging these files.
- Aggressive optimization or script-concatenation plugins could alter the tested dependency order.
- The daily persistence boundary continues to follow the implementation’s UTC-derived date.
- Video remains established in the earlier single-background mode, not as a fully validated multi-region generative medium.
- No Phase 6 algorithm family has been selected; adding more before observing the present twelve would create catalogue growth without evidence of a real visual need.
What AI assisted, what tools proved, and what remained my decision
This work developed through an unusually direct human–AI loop. AI helped propose the Generative Engine architecture, write candidate generators, compose patches, build validation scripts and compare mathematical approaches. It could generate a substantial amount of source quickly, but speed did not give that source authority over production.
I remained the person deciding what the system should mean. The clearest example was record identity. An initial technical observation described two records sharing a URL as a possible duplication wrinkle. I clarified that the duplication was deliberate because different dimensions produced different views. That factual correction changed the engine’s identity model, persistence signature and recovery logic.
The live environment supplied the next level of evidence. GNU patch reported whether hunks matched. Node parsed JavaScript. PHP parsed the extension. D3 generated actual geometry. WordPress loaded the theme and returned saved settings. Browser state established which algorithm visitors really received. When one of those results contradicted an earlier assumption, the assumption changed.
This division of responsibility is, I think, the most useful model for AI-assisted systems work:
- AI can generate and compare candidate solutions.
- The operator defines intent, acceptable risk and visual meaning.
- Deterministic tools verify syntax and structure.
- Executable tests verify behaviour.
- The production environment establishes operational fact.
- Backups preserve the ability to reconsider.
There is also an ethical dimension to inspectability. A compressed Base64 installer may be convenient for transport, yet it weakens a person’s ability to understand what a privileged command will do. Replacing it with plain Bash heredocs made the collaboration slower to scroll through and easier to govern. I consider that a good trade.
The generative system itself offers a smaller conceptual lesson. Randomness became useful only after identity, limits, persistence and responsibility were made explicit. The same is true of collaborative development: creativity expands the space of possibilities, while evidence and reversible procedure keep those possibilities inhabitable.
Practical lessons from the complete iteration
- Extract a stable interface before multiplying layouts. A generator that returns normalized regions is easier to extend and test than another complete renderer.
- Define identity from the domain. A URL was insufficient because record dimensions carried intentional visual meaning.
- Separate random streams by responsibility. Selection, geometry, palette and complexity can remain reproducible without being unnecessarily coupled.
- Use mature libraries for mature geometry. D3 Delaunay solved Voronoi and triangulation robustly; small recurrences remained clearer as local code.
- Do not install infrastructure merely to satisfy an installer assumption. The missing npm executable did not mean the application required npm in production.
- Choose patching tools according to source certainty. Unified diffs suit known multi-file changes; assertion-based Python transformations suit semantic anchor insertion.
- Test semantics as well as bytes. Checksums identify known candidates, while geometry tests establish bounds, coverage, counts and determinism.
- Protect settings independently from source files. A successful code deployment must not imply permission to rewrite saved configuration.
- Keep public labels conceptually honest. Automatic mode should reveal the actual algorithm it selected.
- Distinguish installation success from runtime success. The browser state and visible badge completed the evidence chain.
- Prefer a failed safe step to a successful partial deployment. Several exit-code-1 results preserved production exactly as designed.
- Stop when the system reaches a coherent state. Twelve working algorithms are a reason to observe, compare and learn before adding a thirteenth.
Conclusion
Yin’s Background Studio reached this stage through a change in abstraction. The media records remained the same kind of records, and the browser still rendered familiar images, GIFs, WebP and SVG assets. What changed was the relationship among them. Geometry became modular, randomness became reproducible, identity became configuration-aware, and automatic selection became observable.
The final production system contains twelve algorithms across five phases, a common generator registry, locally served D3 Delaunay geometry, manual and automatic complexity, deterministic seeds, protected WordPress settings and a brutalist badge that names the actual result. It runs on the same small VPS because the server distributes the vocabulary while the browser performs the composition.
Honestly, the most valuable result may be methodological. The system did not emerge from one perfect reconstruction. It developed through bounded changes, failed assumptions, corrected diagnostics and increasingly precise tests. Mathematics gave the background more possibilities; disciplined deployment allowed those possibilities to reach a real website without sacrificing the work that already functioned.
