Maze Growth
Free · MITGenerative maze that grows outward from a seed cell, drawing itself as a living background. Canvas-based, self-contained, pauses when off screen and stops entirely under prefers-reduced-motion.
- Category
- Backgrounds
- Framework
- astro
- Version
- 1.0.1
- Peer deps
- none
Install
velocityx add maze-growth
Free and MIT licensed — no account needed. The file lands under src/components/velocity/maze-growth/ by default (override with --dest), and it's yours to edit from the moment it's written.
Demo
View the live demo on the showcase site ↗
Source
Pulled live from the registry — this is the exact file velocityx add maze-growth writes into your project right now.
components/velocity/maze-growth/MazeGrowth.astro
---
// VELOCITY COMPONENT · theming
//
// Colours here are Tailwind `primary-*` / `accent-*`, which resolve through
// the Velocity preset. Out of the box they are a neutral indigo — NOT the
// Aidxn brand — so this drops into your project without dragging our look in.
//
// ⚡ NOT USING VELOCITY? Run this once, in your project root:
//
// velocityx theme
//
// It writes velocity-preset.cjs + velocity-theme.css. Add the preset to your
// tailwind.config, import the css, then set your whole brand in ONE place:
//
// :root {
// --vx-brand: custom;
// --vx-primary-600: 220 38 38; <- your action colour
// --vx-primary-700: 185 28 28;
// }
//
// ⚠️ Raw RGB channels, space separated — NOT hex. `#DC2626` here silently
// breaks every /opacity variant in the library (bg-primary-600/20 etc).
//
// Prefer not to install anything? Find-and-replace `primary-` with your own
// scale name. Nothing else in this file depends on Velocity.
/**
* MazeGrowth — a fast "growth" that draws itself through a geometric maze,
* rendered as pure SVG so it's trivially swappable.
*
* The default art is a generated truchet maze (quarter-arc tiles → flowing
* corridors). The reveal is a stroke-dashoffset draw with `pathLength="1"`,
* so it works on ANY SVG path regardless of length — pass your own `paths`
* (array of `d` strings) to swap the maze for your own SVG and it'll grow the
* same way. Rows are staggered so growth spreads through the maze in lots.
*
* Props:
* paths? — custom SVG path `d` strings (defaults to a generated maze)
* viewBox? — SVG viewBox (match your custom art)
* duration? — seconds per draw cycle (lower = faster). Default 2.4.
* stagger? — seconds between each path's start. Default 0.16.
*/
interface Props {
paths?: string[];
viewBox?: string;
duration?: number;
stagger?: number;
class?: string;
}
const COLS = 12;
const ROWS = 8;
const S = 40;
function genMazeRows(): string[] {
const rows: string[] = [];
for (let r = 0; r < ROWS; r++) {
let d = '';
for (let c = 0; c < COLS; c++) {
const x = c * S;
const y = r * S;
const h = S / 2;
// deterministic truchet tile choice → connected maze corridors
const flip = ((r * 7 + c * 13 + r * c) % 5) < 3;
if (flip) {
d += `M${x + h} ${y}A${h} ${h} 0 0 1 ${x + S} ${y + h}`;
d += `M${x} ${y + h}A${h} ${h} 0 0 1 ${x + h} ${y + S}`;
} else {
d += `M${x + h} ${y}A${h} ${h} 0 0 0 ${x} ${y + h}`;
d += `M${x + S} ${y + h}A${h} ${h} 0 0 0 ${x + h} ${y + S}`;
}
}
rows.push(d);
}
return rows;
}
const {
paths,
viewBox = `0 0 ${COLS * S} ${ROWS * S}`,
duration = 2.4,
stagger = 0.16,
class: className = '',
} = Astro.props;
const finalPaths = paths && paths.length ? paths : genMazeRows();
---
<div class:list={['mz-wrap', className]}>
<svg viewBox={viewBox} preserveAspectRatio="xMidYMid slice" class="mz-svg" aria-hidden="true">
{
finalPaths.map((d, i) => (
<g style={`animation-delay:${(i * stagger).toFixed(2)}s`}>
<path d={d} pathLength="1" class="mz-glow" style={`animation-delay:${(i * stagger).toFixed(2)}s`} />
<path d={d} pathLength="1" class="mz-line" style={`animation-delay:${(i * stagger).toFixed(2)}s`} />
</g>
))
}
</svg>
</div>
<style define:vars={{ mzDur: `${duration}s` }}>
.mz-wrap { width: 100%; height: 100%; }
.mz-svg { display: block; width: 100%; height: 100%; }
.mz-line,
.mz-glow {
fill: none;
stroke-linecap: round;
stroke-dasharray: 1;
stroke-dashoffset: 1;
animation: mz-draw var(--mzDur) linear infinite;
}
.mz-line { stroke: var(--primary-500); stroke-width: 2.4; }
.mz-glow { stroke: var(--primary-400); stroke-width: 8; opacity: 0.45; filter: blur(3px); }
/* draw in (0→50%), then keep flowing out the tail (→100%) for a continuous run */
@keyframes mz-draw {
0% { stroke-dashoffset: 1; }
50% { stroke-dashoffset: 0; }
100% { stroke-dashoffset: -1; }
}
@media (prefers-reduced-motion: reduce) {
.mz-line, .mz-glow { animation: none; stroke-dashoffset: 0; }
}
</style>
Theming
Uses the shared primary-* / accent-* scale — see Theming if colours look unstyled after install.