Four Web Components that draw a building scene from a seed and plot it in like a pen plotter. Vanilla JS, no dependencies, one colour, one pixel wide. Every feature is a flag; every scene takes a perspective mode through one pinhole camera.
Give an element a box and it fills it. With no seed set, it hashes the clock, so every visit is a sibling. In bounce mode it plots in, holds, plots out, and draws the next one. The Night button switches the whole page and every drawing on it, and lights the windows.
<dmg-street-corner> with defaults. Reload for another.
The combination a site puts on a black band under its last call to action: a two-point skyline, night, glowing windows, bouncing and reseeding. night sets the page colour to black by itself, so the element needs no page attribute to sit on a dark band.
<dmg-skyline perspective="2" night glow mode="bounce" reseed="cycle">
Script tag, no bundler. The file registers all four elements and exposes the generators on window.ProceduralLines.
<script src="procedural-line-renderings.js"></script>
<dmg-street-corner style="width: 100%; height: 60vh"></dmg-street-corner>
<dmg-street-corner preset="ink" perspective="3"></dmg-street-corner>
<dmg-massing seed="42" mode="once" perspective="2"></dmg-massing>
<dmg-skyline night perspective="1"></dmg-skyline>
<dmg-street-corner options='{"shutters":true,"pediments":true,"trees":false}' glow="#F2D08A"></dmg-street-corner>
<dmg-street-corner randomize="0.5"></dmg-street-corner>
<dmg-massing options='{"winArch":true,"quoins":true,"stoop":true,"fence":true,"kerb":true}'></dmg-massing>
ES modules, from the repository's src/:
import '@dmg-venture-studio/procedural-line-renderings';
import { corner4, skyline, rng, bounce } from '@dmg-venture-studio/procedural-line-renderings';
Every playground below writes the exact tag and the equivalent API call for whatever you have switched on. Copy either.
<dmg-street-corner>Two rows of buildings meeting at a corner, seen through a pinhole camera. Facades come from a split grammar with five window families, rooftop objects, gaps in the street wall, ornament, and twelve kinds of millwork. The street in front carries a kerb, road dashes, lamp posts, and trees grown by a branching rule. The ink preset adds what a pen-and-ink elevation has: curved bay windows over several floors, a rounded corner with windows wrapping it, a deep bracketed cornice with a hatched soffit, shade hatching in the reveals and under every projection, an iron fence with a gate at each door, stoops with cheek walls, planters with foliage, and round arches. Perspective 2 keeps verticals vertical; 3 pitches the camera up.
<dmg-massing>A massing model: stepped terraces with railings and stairs, towers with floor lines and mullions, courtyard footprints, cantilevers on columns, gable and mansard roofs, a drawn site, an exploded view, and a light study in three hatch weights. Axonometric by default; perspective 2 puts the pinhole camera above and outside the cluster, 3 puts it low and pitched up. Hidden lines are removed by painting far to near, and the underside of a raised box appears when the camera stands below it.
The massing also carries the street corner's facade families. Switch on any window, ornament, millwork, ink, depth or roof flag and every non-tower box gets a facade on its two visible faces, drawn with the corner's own grammar scaled so one box unit is one floor: window families, quoins, string courses, dentils, pediments, pilasters, fire escapes, curved bay windows, deep cornices, shade hatching, balconies, awnings, stoops and planters at ground level, rooftop objects. The street flags lay a kerb, road dashes, lamps, trees and the iron fence along the plot's two street edges, with a gate at every door.
<dmg-skyline>A 3D city block. Lots on a grid, or on both sides of an avenue for one-point, each carrying a tower of stacked setback tiers with a crown (spire, dome, mast, columned cap) or a low podium. Only camera-facing faces are drawn, filled far to near, with a line at every floor and mullions on glass, which is what makes a skyline read as glazing in ink. Perspective 1 looks down the avenue, 2 stands at a corner outside the block, 3 stands between towers and looks up.
<dmg-plan>A floor plate split into rooms by recursive subdivision, walls with thickness, a door swing cut into every partition, a window in the outer wall for every room that touches it, hatched wall poche, and one stair. It has no windows to light, so it ignores glow.
| Attribute | Values | Default | Meaning |
|---|---|---|---|
seed | integer | none | Fixed seed. When absent, a hash of the clock is used. |
reseed | cycle, never | cycle | In bounce mode with no fixed seed, draw a new sibling at the bottom of each cycle. |
mode | bounce, once | bounce | Plot in, hold, plot out, repeat; or plot in once and stop. |
perspective | corner 2 3; massing 0 2 3; skyline 1 2 3 | 2, 0, 2 | The camera model, all through one pinhole camera. The corner rejects 1 with a console warning and falls back to 2. Not on the plan. |
preset | ink | none | Street corner only: a named bundle of flags. Flags in options override it. |
options | JSON object | none | Feature flags for this component, see the tables below. Only the flags you set are needed. |
randomize | fraction 0 to 1 | off | On every seed, choose a random subset of the flags with coverage drawn between this fraction and 1. Flags set in options are pinned and camera flags are never touched. The chosen set is on element.resolvedOptions and in the seed event. |
color | CSS colour | #C4C6CB, night #A8AAB0 | Stroke colour. |
page | CSS colour | #FFFFFF, night #0E0E10 | Page colour used for hidden-line fills. Match your background. |
glow | hex colour | off, night #F2D08A | Light a random subset of windows in this colour with a soft shadow. Ignored by the plan. |
night | boolean | off | Dark page, light lines, glowing windows on by default and more of them. The page's Night button sets it on every element that follows the page. |
pitch | boolean | off | Street corner shorthand for perspective="3". |
full | boolean | off | Street corner shorthand for back row, balconies, awnings, setbacks, close camera. |
| Member | What it does |
|---|---|
element.regenerate() | Draws a new sibling from a fresh clock seed. |
element.stop() | Halts the animation and leaves the last frame. |
element.seed | The seed of the drawing on screen. Setting it redraws. |
element.options | The declared flags and params with defaults filled in, before any randomized draw. |
element.resolvedOptions | The flags actually drawn, with any randomized draw applied. Null before the first draw. |
element.snippet | The HTML tag that reproduces the current state, non-default flags only. |
seed event | Fires on every draw with event.detail.seed and event.detail.options, the resolved flags. |
Each table is generated from the library's own option schema, so it cannot drift from the code.
| Flag | Family | Default | Meaning |
|---|
| Flag | Family | Default | Meaning |
|---|
| Flag | Family | Default | Meaning |
|---|
| Flag | Family | Default | Meaning |
|---|
The element is display: block with a 240px minimum height. Size it with CSS or a grid cell; it redraws when its box changes. Fixed sizes work for known layouts; for a responsive box set a width and an aspect-ratio. The drawing is fitted to the box, so keep text out of it. The element never measures its siblings.
Night is two things. night on an element switches that element. The Night button in the header switches the page tokens and every element marked to follow the page, and each playground can still say day or night for itself. The choice is remembered in this browser.
A cycle plots in over about seven seconds, holds five, plots out over four, and pauses briefly. Under prefers-reduced-motion: reduce the element draws the finished state once and never animates. Glowing windows redraw every frame with a canvas shadow, which costs more on large boxes; prefer mode="once" when many are lit.
Projection is real geometry, never faked. One-, two- and three-point perspective are the same pinhole camera with unit focal length, placed and aimed differently. Looking straight down an avenue makes every line along it converge on one point. Turning the level camera to a corner gives two points and keeps verticals vertical. Pitching it up adds a third point above the page. The result is fitted to the box with a uniform scale and translation; a similarity preserves perspective, so perspective strength comes only from how far the camera stands from the buildings. The massing keeps a parallel axonometric projection as its default.
Every box fills its camera-facing faces in the page colour before stroking its edges, and boxes are drawn far to near, so nearer boxes erase what they cover. Whether a face faces the camera is a dot product of its normal with the direction to the camera; a face below the eye shows its underside. Projecting pieces of a facade, a bay window, a cornice, a stoop, are their own items drawn right after the wall they sit on, with fills, or the wall's lines would show through them. Gable roofs replace the top face with two slopes; exploded views lift boxes and draw dashed guides.
Facades come from a split grammar in the tradition of Instant Architecture and CGA shape: a lot becomes a building, a building becomes floors, floors become bays, bays become windows from one of five families, and trim is applied per opening from inside that loop. The skyline is a city block: lots on a grid, each with a tower of stacked setback tiers and a crown, every visible face carrying a line per floor. Shade in the ink preset is parallel line fields under every projection and in the reveals, with a sun side chosen per seed.
Everything is a pure function of a 32-bit seed from a mulberry32 generator, so a drawing can be named by its seed and reproduced anywhere. Plotting animates by stroke length: a progress renderer draws the first p pixels of the stroke list each frame, with each item's fills and lit windows appearing as the item starts, so hidden-line removal holds mid-plot.
The vocabulary, the references, and the sources behind the millwork and the ink features are in research.md.
The repository is a Claude Code plugin. Its skill teaches the math, the composition rules, and how to use these components.
/plugin marketplace add DMG-Venture-Studio/procedural-line-renderings
/plugin install procedural-line-renderings
Or copy skills/procedural-architecture-lines/ into a project's .claude/skills/. Instructions for working on the repository itself are in its CLAUDE.md.