2026-02-19T11-57-39_auto_memory/memories.db-shm, memory/memories.db-wal, me

This commit is contained in:
Nicholai Vogel 2026-02-19 04:57:39 -07:00
parent 63352f6df1
commit f0a945f17a
3 changed files with 839 additions and 8 deletions

View File

@ -1221,3 +1221,11 @@
{"timestamp":"2026-02-19T11:48:54.952Z","level":"info","category":"watcher","message":"File added","data":{"path":"/home/nicholai/.agents/memory/memories.db-wal"}}
{"timestamp":"2026-02-19T11:49:03.513Z","level":"info","category":"git","message":"Auto-committed","data":{"message":"2026-02-19T11-48-59_auto_memory/memories.db-shm, memory/vectors.zvec/idmap.","filesChanged":56}}
{"timestamp":"2026-02-19T11:49:09.301Z","level":"info","category":"watcher","message":"File changed","data":{"path":"/home/nicholai/.agents/memory/memories.db-shm"}}
{"timestamp":"2026-02-19T11:49:14.318Z","level":"info","category":"git","message":"Auto-committed","data":{"message":"2026-02-19T11-49-14_auto_memory/memories.db-shm","filesChanged":1}}
{"timestamp":"2026-02-19T11:52:12.012Z","level":"warn","category":"git","message":"Periodic sync failed: No git credentials found. Run `gh auth login` or set GITHUB_TOKEN secret."}
{"timestamp":"2026-02-19T11:57:12.011Z","level":"warn","category":"git","message":"Periodic sync failed: No git credentials found. Run `gh auth login` or set GITHUB_TOKEN secret."}
{"timestamp":"2026-02-19T11:57:30.231Z","level":"info","category":"watcher","message":"File added","data":{"path":"/home/nicholai/.agents/memory/memories.db-shm"}}
{"timestamp":"2026-02-19T11:57:30.231Z","level":"info","category":"watcher","message":"File added","data":{"path":"/home/nicholai/.agents/memory/memories.db-wal"}}
{"timestamp":"2026-02-19T11:57:30.246Z","level":"info","category":"watcher","message":"File changed","data":{"path":"/home/nicholai/.agents/memory/memories.db-shm"}}
{"timestamp":"2026-02-19T11:57:30.489Z","level":"info","category":"watcher","message":"File changed","data":{"path":"/home/nicholai/.agents/memory/memories.db-shm"}}
{"timestamp":"2026-02-19T11:57:34.868Z","level":"info","category":"watcher","message":"File changed","data":{"path":"/home/nicholai/.agents/memory/memories.db-shm"}}

View File

@ -1,5 +1,6 @@
/* === Design System Globals === */
/* --- Dark theme (default) --- */
:root {
--color-bg: #08080a;
--color-surface: #0e0e12;
@ -13,6 +14,8 @@
--color-accent-hover: #c0c0c8;
--color-danger: #8a4a48;
--color-success: #4a7a5e;
--color-dither: #f0f0f2;
--color-grain-opacity: 0.04;
--font-mono: 'IBM Plex Mono', monospace;
@ -28,6 +31,24 @@
--dur: 0.2s;
}
/* --- Light theme --- */
[data-theme="light"] {
--color-bg: #e4dfd8;
--color-surface: #dbd5cd;
--color-surface-raised: #d1cbc2;
--color-border: rgba(0, 0, 0, 0.06);
--color-border-strong: rgba(0, 0, 0, 0.12);
--color-text: #2a2a2e;
--color-text-bright: #0a0a0c;
--color-text-muted: #9a968f;
--color-accent: #6a6660;
--color-accent-hover: #3a3832;
--color-danger: #8a4a48;
--color-success: #4a7a5e;
--color-dither: #0a0a0c;
--color-grain-opacity: 0.06;
}
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
@ -42,6 +63,7 @@ body {
line-height: 1.55;
-webkit-font-smoothing: antialiased;
overflow-x: hidden;
transition: background 0.4s var(--ease), color 0.4s var(--ease);
}
/* Grain */
@ -51,7 +73,7 @@ body::before {
inset: 0;
z-index: 9999;
pointer-events: none;
opacity: 0.04;
opacity: var(--color-grain-opacity);
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 512 512' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.2' numOctaves='5' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
background-repeat: repeat;
background-size: 256px;
@ -99,7 +121,7 @@ code, .mono {
text-transform: uppercase;
letter-spacing: 0.14em;
color: var(--color-bg);
background: var(--color-text);
background: var(--color-text-bright);
padding: 2px 8px 1px;
}
@ -128,6 +150,23 @@ code, .mono {
top: 50%;
}
/* === 4-pointed star === */
.star-4 {
display: inline-block;
width: 12px;
height: 12px;
flex-shrink: 0;
background: var(--color-text-muted);
clip-path: polygon(
50% 0%, 60% 35%, 100% 50%, 60% 65%,
50% 100%, 40% 65%, 0% 50%, 40% 35%
);
}
.star-4--bright {
background: var(--color-text-bright);
}
/* === Buttons === */
.btn {
@ -155,7 +194,7 @@ code, .mono {
.btn:active { opacity: 0.8; }
.btn-primary {
border-color: var(--color-text);
border-color: var(--color-text-bright);
color: var(--color-text-bright);
}
.btn-primary:hover {
@ -205,12 +244,11 @@ code, .mono {
transition: border-color var(--dur) var(--ease);
}
.input::placeholder { color: var(--color-text-muted); }
.input:focus { border-color: var(--color-text); }
.input:focus { border-color: var(--color-text-bright); }
select.input {
appearance: none;
cursor: pointer;
background-image: url("data:image/svg+xml,%3Csvg width='8' height='5' viewBox='0 0 8 5' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1L4 4L7 1' stroke='%233e3e46' stroke-width='1' stroke-linecap='square'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 12px center;
padding-right: 32px;
@ -234,10 +272,14 @@ select.input {
}
.badge-accent {
color: var(--color-text-bright);
border-color: var(--color-text);
border-color: var(--color-text-bright);
background: rgba(255, 255, 255, 0.04);
}
[data-theme="light"] .badge-accent {
background: rgba(0, 0, 0, 0.04);
}
/* === Card === */
.card {
@ -247,7 +289,7 @@ select.input {
padding: var(--space-lg);
transition: border-color var(--dur) var(--ease);
}
.card:hover { border-color: rgba(255, 255, 255, 0.2); }
.card:hover { border-color: var(--color-text-muted); }
/* === Vertical text === */
@ -258,7 +300,7 @@ select.input {
font-size: 0.625rem;
letter-spacing: 0.2em;
text-transform: uppercase;
color: rgba(255, 255, 255, 0.1);
color: var(--color-border-strong);
}
/* === Utilities === */

View File

@ -0,0 +1,781 @@
<!DOCTYPE html>
<html lang="en" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Design Brief</title>
<link rel="stylesheet" href="globals.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
.page {
position: relative;
z-index: 1;
max-width: 820px;
margin: 0 auto;
padding: var(--space-xl) var(--space-lg);
}
/* === Theme toggle === */
.theme-toggle {
position: fixed;
top: 20px;
right: 24px;
z-index: 100;
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
background: none;
border: 1px solid var(--color-border-strong);
padding: 6px 12px;
font-family: var(--font-mono);
font-size: 0.5625rem;
font-weight: 500;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--color-text-muted);
transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.theme-toggle:hover {
color: var(--color-text);
border-color: var(--color-text-muted);
}
.theme-toggle .toggle-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--color-text-muted);
transition: background var(--dur) var(--ease);
}
.theme-toggle:hover .toggle-dot {
background: var(--color-text);
}
/* === Hero zone with dithered canvas === */
.hero-zone {
position: relative;
min-height: 360px;
margin-bottom: var(--space-2xl);
display: flex;
align-items: flex-end;
}
.hero-canvas-wrap {
position: absolute;
inset: 0;
overflow: hidden;
}
#dither-canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0.35;
}
.hero-content {
position: relative;
z-index: 2;
padding-bottom: var(--space-lg);
}
.hero-title {
font-size: 3.5rem;
letter-spacing: 0.08em;
line-height: 0.92;
color: var(--color-text-bright);
text-transform: uppercase;
font-weight: 400;
}
.hero-sub {
font-size: 0.75rem;
color: var(--color-text-muted);
margin-top: var(--space-sm);
letter-spacing: 0.03em;
}
/* === Schematic decorators === */
.schematic-circle {
position: absolute;
border: 1px solid var(--color-border-strong);
border-radius: 50%;
pointer-events: none;
}
.schematic-circle::before,
.schematic-circle::after {
content: '';
position: absolute;
background: var(--color-border-strong);
}
.schematic-circle::before {
width: 1px;
height: 8px;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.schematic-circle::after {
width: 8px;
height: 1px;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.connector-line {
position: absolute;
pointer-events: none;
}
.connector-line--v {
width: 1px;
background: var(--color-border);
}
.connector-line--h {
height: 1px;
background: var(--color-border);
}
.connector-line .dot {
position: absolute;
width: 3px;
height: 3px;
border-radius: 50%;
background: var(--color-text-muted);
}
/* Vertical sidebar text */
.sidebar-vert {
position: fixed;
left: 24px;
top: 50%;
transform: translateY(-50%);
z-index: 2;
}
/* Right side dithered edge bleed */
#dither-edge {
position: fixed;
right: 0;
top: 0;
width: 240px;
height: 100vh;
opacity: 0.18;
z-index: 0;
pointer-events: none;
}
/* === Sections === */
.section {
padding-bottom: var(--space-xl);
margin-bottom: var(--space-xl);
border-bottom: 1px solid var(--color-border);
}
.section:last-child {
border-bottom: none;
margin-bottom: 0;
}
.section-content { margin-top: var(--space-md); }
.section-header {
display: flex;
align-items: center;
gap: var(--space-sm);
}
/* === Swatches === */
.swatch {
width: 32px;
height: 32px;
border-radius: 50%;
border: 1px solid var(--color-border-strong);
}
.swatch-label {
font-family: var(--font-mono);
font-size: 0.5rem;
color: var(--color-text-muted);
letter-spacing: 0.08em;
text-transform: uppercase;
}
/* Type specimen */
.type-specimen {
border-left: 1px solid var(--color-border-strong);
padding-left: var(--space-md);
}
.code-block {
font-family: var(--font-mono);
font-size: 0.6875rem;
color: var(--color-text-muted);
padding: var(--space-sm) var(--space-md);
background: var(--color-surface);
border: 1px solid var(--color-border);
display: block;
letter-spacing: 0.03em;
}
.tri {
display: inline-block;
width: 0;
height: 0;
border-left: 4px solid currentColor;
border-top: 2.5px solid transparent;
border-bottom: 2.5px solid transparent;
flex-shrink: 0;
}
/* === Dither block — bold compositional element === */
.dither-block {
position: relative;
overflow: hidden;
background: var(--color-surface);
}
.dither-block canvas {
display: block;
width: 100%;
height: 100%;
}
.dither-block--hero-side {
margin: var(--space-lg) 0;
height: 120px;
}
.dither-block--section {
height: 80px;
margin: var(--space-md) 0 var(--space-sm);
}
.dither-block--wide {
height: 160px;
margin: var(--space-lg) 0;
}
/* === Numbered index === */
.num-index {
display: grid;
grid-template-columns: repeat(8, 1fr);
gap: 3px 6px;
font-family: var(--font-mono);
font-size: 0.625rem;
color: var(--color-text-muted);
letter-spacing: 0.06em;
max-width: 240px;
}
.num-index span {
text-align: right;
padding: 2px 0;
}
/* === Inline dithered section art (legacy, now bolder) === */
.dither-inline {
height: 80px;
margin: var(--space-md) 0 var(--space-sm);
opacity: 0.8;
overflow: hidden;
}
/* === Editorial asymmetric offset === */
.offset-right {
margin-left: auto;
margin-right: 0;
}
/* === Footer bar === */
.footer-bar {
display: flex;
justify-content: space-between;
align-items: flex-end;
margin-top: var(--space-2xl);
padding-top: var(--space-lg);
border-top: 1px solid var(--color-border);
}
/* === Crosshair field === */
.crosshair-field {
position: fixed;
inset: 0;
pointer-events: none;
z-index: 0;
}
.crosshair-field .ch {
position: absolute;
width: 12px;
height: 12px;
}
.crosshair-field .ch::before,
.crosshair-field .ch::after {
content: '';
position: absolute;
background: var(--color-border);
}
.crosshair-field .ch::before {
width: 1px;
height: 100%;
left: 50%;
}
.crosshair-field .ch::after {
width: 100%;
height: 1px;
top: 50%;
}
/* === Schematic overlay (fixed background) === */
.schematic-overlay {
position: fixed;
inset: 0;
pointer-events: none;
z-index: 0;
overflow: hidden;
}
/* Star symbols */
.star-marker {
position: absolute;
font-size: 0.5rem;
letter-spacing: 0.12em;
color: var(--color-border-strong);
font-family: var(--font-mono);
}
</style>
</head>
<body>
<!-- Theme toggle -->
<button class="theme-toggle" id="theme-toggle" aria-label="Toggle theme">
<span class="toggle-dot"></span>
<span class="toggle-label">Dark</span>
</button>
<!-- Background layers -->
<div class="schematic-overlay">
<!-- Schematic circles -->
<div class="schematic-circle" style="width: 80px; height: 80px; top: 8%; right: 12%;"></div>
<div class="schematic-circle" style="width: 120px; height: 120px; top: 45%; left: 4%;"></div>
<div class="schematic-circle" style="width: 50px; height: 50px; bottom: 20%; right: 25%;"></div>
<!-- Connector lines -->
<div class="connector-line connector-line--v" style="left: 8%; top: 15%; height: 200px;">
<span class="dot" style="top: 0;"></span>
<span class="dot" style="bottom: 0;"></span>
</div>
<div class="connector-line connector-line--h" style="top: 32%; left: 5%; width: 140px;">
<span class="dot" style="left: 0;"></span>
<span class="dot" style="right: 0;"></span>
</div>
<div class="connector-line connector-line--v" style="right: 15%; top: 60%; height: 160px;">
<span class="dot" style="top: 0;"></span>
<span class="dot" style="bottom: 0;"></span>
</div>
<!-- Star markers -->
<span class="star-marker" style="top: 6%; left: 3%;">****</span>
<span class="star-marker" style="top: 6%; right: 3%;">****</span>
<span class="star-marker" style="bottom: 4%; left: 3%;">****</span>
</div>
<div class="crosshair-field">
<div class="ch" style="top: 10%; left: 15%;"></div>
<div class="ch" style="top: 10%; left: 55%;"></div>
<div class="ch" style="top: 10%; left: 85%;"></div>
<div class="ch" style="top: 35%; left: 30%;"></div>
<div class="ch" style="top: 35%; left: 70%;"></div>
<div class="ch" style="top: 60%; left: 10%;"></div>
<div class="ch" style="top: 60%; left: 50%;"></div>
<div class="ch" style="top: 60%; left: 90%;"></div>
<div class="ch" style="top: 85%; left: 25%;"></div>
<div class="ch" style="top: 85%; left: 75%;"></div>
</div>
<!-- Right edge dithered bleed -->
<canvas id="dither-edge"></canvas>
<!-- Vertical sidebar -->
<div class="sidebar-vert">
<span class="vertical-text">Sys_Component_Library_v01</span>
</div>
<div class="page">
<!-- Hero -->
<div class="hero-zone">
<div class="hero-canvas-wrap">
<canvas id="dither-canvas"></canvas>
</div>
<div class="hero-content">
<div class="flex items-center gap-sm" style="margin-bottom: var(--space-md);">
<span class="label-inv">SYS</span>
<span class="star-4"></span>
<span class="label">Design_Brief</span>
</div>
<div class="hero-title">Component<br>Library</div>
<p class="hero-sub">Core UI primitives and tokens.</p>
</div>
</div>
<!-- Bold dither block — compositional anchor -->
<div class="dither-block dither-block--wide">
<canvas data-dither="glitch-wide"></canvas>
</div>
<!-- Colors -->
<div class="section">
<div class="section-header">
<div class="crosshair"></div>
<span class="label">Sys_Palette</span>
<span class="star-4" style="margin-left: auto;"></span>
</div>
<div class="flex gap-lg wrap section-content">
<div class="flex flex-col gap-xs items-center">
<div class="swatch" style="background: var(--color-bg);"></div>
<span class="swatch-label">bg</span>
</div>
<div class="flex flex-col gap-xs items-center">
<div class="swatch" style="background: var(--color-surface);"></div>
<span class="swatch-label">srf</span>
</div>
<div class="flex flex-col gap-xs items-center">
<div class="swatch" style="background: var(--color-surface-raised);"></div>
<span class="swatch-label">rsd</span>
</div>
<div class="flex flex-col gap-xs items-center">
<div class="swatch" style="background: var(--color-border-strong);"></div>
<span class="swatch-label">brd</span>
</div>
<div class="flex flex-col gap-xs items-center">
<div class="swatch" style="background: var(--color-text);"></div>
<span class="swatch-label">txt</span>
</div>
<div class="flex flex-col gap-xs items-center">
<div class="swatch" style="background: var(--color-text-muted);"></div>
<span class="swatch-label">mtd</span>
</div>
<div class="flex flex-col gap-xs items-center">
<div class="swatch" style="background: var(--color-accent);"></div>
<span class="swatch-label">acc</span>
</div>
<div class="flex flex-col gap-xs items-center">
<div class="swatch" style="background: var(--color-danger);"></div>
<span class="swatch-label">err</span>
</div>
<div class="flex flex-col gap-xs items-center">
<div class="swatch" style="background: var(--color-success);"></div>
<span class="swatch-label">ok</span>
</div>
</div>
</div>
<!-- Typography -->
<div class="section">
<div class="section-header">
<div class="crosshair"></div>
<span class="label">Sys_Type</span>
</div>
<div class="dither-block dither-block--section">
<canvas data-dither="band"></canvas>
</div>
<div class="flex flex-col gap-md section-content type-specimen">
<h1>Heading One</h1>
<h2>Heading Two</h2>
<h3>Heading Three</h3>
<p>Body text in muted color for secondary content.</p>
<span class="code-block">monospace for code and data</span>
</div>
</div>
<!-- Buttons -->
<div class="section">
<div class="section-header">
<div class="crosshair"></div>
<span class="label">Sys_Buttons</span>
<span class="star-4" style="margin-left: auto;"></span>
<span class="star-4"></span>
</div>
<div class="flex gap-md wrap items-center section-content">
<button class="btn btn-primary">Primary</button>
<button class="btn btn-secondary">Secondary</button>
<button class="btn btn-ghost">Ghost</button>
<button class="btn btn-danger">Danger</button>
<button class="btn btn-sm btn-secondary">Small</button>
</div>
</div>
<!-- Badges -->
<div class="section">
<div class="section-header">
<div class="crosshair"></div>
<span class="label">Sys_Badges</span>
</div>
<div class="flex gap-sm wrap items-center section-content">
<span class="badge">Default</span>
<span class="badge badge-accent">Accent</span>
<span class="label-inv" style="margin-left: 4px;">Inverted</span>
</div>
</div>
<!-- Dither block between sections -->
<div class="dither-block dither-block--section">
<canvas data-dither="smear"></canvas>
</div>
<!-- Inputs — offset right for asymmetry -->
<div class="section">
<div class="section-header">
<div class="crosshair"></div>
<span class="label">Sys_Form</span>
</div>
<div class="flex flex-col gap-md section-content offset-right" style="max-width: 340px;">
<input class="input" type="text" placeholder="Text input...">
<select class="input">
<option>Select an option</option>
<option>Option A</option>
<option>Option B</option>
</select>
</div>
</div>
<!-- Card -->
<div class="section">
<div class="section-header">
<div class="crosshair"></div>
<span class="label">Sys_Card</span>
</div>
<div class="dither-block dither-block--section">
<canvas data-dither="cloud"></canvas>
</div>
<div class="card section-content" style="max-width: 340px;">
<div class="flex flex-col gap-md">
<div class="flex items-center gap-sm">
<h3>Card Title</h3>
<span class="badge badge-accent">Tag</span>
</div>
<p>A surface container for grouped content.</p>
<div class="flex gap-sm">
<button class="btn btn-sm btn-primary">
<span class="tri"></span>
Action
</button>
<button class="btn btn-sm btn-ghost">Cancel</button>
</div>
</div>
</div>
</div>
<!-- Footer with numbered index -->
<div class="footer-bar">
<div class="flex items-center gap-sm">
<span class="star-4"></span>
<span class="label">Sys_Index</span>
</div>
<div class="num-index">
<span>00</span><span>01</span><span>02</span><span>03</span>
<span>04</span><span>05</span><span>06</span><span>07</span>
<span>08</span><span>09</span><span>10</span><span>11</span>
<span>12</span><span>13</span><span>14</span><span>15</span>
<span>16</span><span>17</span><span>18</span><span>19</span>
<span>20</span><span>21</span><span>22</span><span>23</span>
</div>
</div>
</div>
<script>
(() => {
// === Theme toggle ===
const toggle = document.getElementById('theme-toggle');
const html = document.documentElement;
const label = toggle.querySelector('.toggle-label');
function setTheme(theme) {
html.setAttribute('data-theme', theme);
label.textContent = theme === 'dark' ? 'Dark' : 'Light';
// Re-render all canvases
setTimeout(renderAllDither, 60);
}
toggle.addEventListener('click', () => {
const current = html.getAttribute('data-theme') || 'dark';
setTheme(current === 'dark' ? 'light' : 'dark');
});
// === Perlin noise ===
const PERM = new Uint8Array(512);
for (let i = 0; i < 256; i++) PERM[i] = i;
// Seeded shuffle for consistency
let seed = 42;
function seededRand() {
seed = (seed * 16807 + 0) % 2147483647;
return (seed - 1) / 2147483646;
}
for (let i = 255; i > 0; i--) {
const j = Math.floor(seededRand() * (i + 1));
[PERM[i], PERM[j]] = [PERM[j], PERM[i]];
}
for (let i = 0; i < 256; i++) PERM[i + 256] = PERM[i];
function fade(t) { return t * t * t * (t * (t * 6 - 15) + 10); }
function lerp(a, b, t) { return a + t * (b - a); }
function grad(hash, x, y) {
const h = hash & 3;
return (h < 2 ? x : -x) + (h === 0 || h === 3 ? y : -y);
}
function noise2d(x, y) {
const xi = Math.floor(x) & 255, yi = Math.floor(y) & 255;
const xf = x - Math.floor(x), yf = y - Math.floor(y);
const u = fade(xf), v = fade(yf);
const aa = PERM[PERM[xi] + yi], ab = PERM[PERM[xi] + yi + 1];
const ba = PERM[PERM[xi + 1] + yi], bb = PERM[PERM[xi + 1] + yi + 1];
return lerp(
lerp(grad(aa, xf, yf), grad(ba, xf - 1, yf), u),
lerp(grad(ab, xf, yf - 1), grad(bb, xf - 1, yf - 1), u), v
);
}
function fbm(x, y, octaves = 4) {
let val = 0, amp = 0.5, freq = 1;
for (let i = 0; i < octaves; i++) {
val += amp * noise2d(x * freq, y * freq);
amp *= 0.5;
freq *= 2;
}
return val;
}
// === Bayer 4x4 ===
const BAYER4 = [
0, 8, 2, 10,
12, 4, 14, 6,
3, 11, 1, 9,
15, 7, 13, 5,
];
function getDitherColor() {
const style = getComputedStyle(document.documentElement);
return style.getPropertyValue('--color-dither').trim() || '#f0f0f2';
}
function ditherCanvas(canvas, noiseFn, pixelSize = 4, threshold = 0.5) {
const rect = canvas.getBoundingClientRect();
const w = Math.floor(rect.width);
const h = Math.floor(rect.height);
if (w === 0 || h === 0) return;
canvas.width = w;
canvas.height = h;
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, w, h);
const cols = Math.floor(w / pixelSize);
const rows = Math.floor(h / pixelSize);
ctx.fillStyle = getDitherColor();
for (let y = 0; y < rows; y++) {
for (let x = 0; x < cols; x++) {
const val = noiseFn(x, y, cols, rows);
const bayerVal = BAYER4[(y % 4) * 4 + (x % 4)] / 16;
if (val + (bayerVal - 0.5) * 0.4 > threshold) {
ctx.fillRect(x * pixelSize, y * pixelSize, pixelSize - 1, pixelSize - 1);
}
}
}
}
// === Glitch/smear dither — vertical streaks like the rave poster ===
function glitchNoise(x, y, cols, rows, offsetX = 0, offsetY = 0) {
const nx = x / cols * 5 + offsetX;
const ny = y / rows * 3 + offsetY;
// Base organic shape
const base = fbm(nx, ny, 5) * 0.5 + 0.5;
// Vertical smear — sample noise with stretched Y
const smearY = y / rows * 0.4;
const smear = fbm(nx * 0.3, smearY + offsetY, 3) * 0.5 + 0.5;
// Horizontal glitch bands
const bandNoise = noise2d(0.1, y / rows * 20 + offsetY) * 0.5 + 0.5;
const glitchShift = bandNoise > 0.65 ? (bandNoise - 0.65) * 8 : 0;
const shiftedBase = fbm(nx + glitchShift, ny, 4) * 0.5 + 0.5;
// Combine
return shiftedBase * 0.5 + smear * 0.3 + base * 0.2;
}
function renderAllDither() {
// Hero canvas
const heroCanvas = document.getElementById('dither-canvas');
if (heroCanvas) {
ditherCanvas(heroCanvas, (x, y, cols, rows) => {
const nx = x / cols * 4;
const ny = y / rows * 4;
const n1 = fbm(nx + 1.3, ny + 0.7, 5);
const n2 = fbm(nx * 1.5 + 5, ny * 1.5 + 3, 4);
const edgeX = Math.min(x / cols, 1 - x / cols) * 2;
const edgeY = Math.min(y / rows, 1 - y / rows) * 2;
const edgeFade = Math.min(edgeX, edgeY);
const combined = (n1 * 0.6 + n2 * 0.4) - edgeFade * 0.3;
return combined * 0.5 + 0.5;
}, 4, 0.46);
}
// Right edge bleed
const edgeCanvas = document.getElementById('dither-edge');
if (edgeCanvas) {
edgeCanvas.style.width = '240px';
edgeCanvas.style.height = '100vh';
ditherCanvas(edgeCanvas, (x, y, cols, rows) => {
const nx = x / cols * 3;
const ny = y / rows * 6;
const n = fbm(nx + 8, ny + 2, 4);
const edgeFade = x / cols;
return (n * 0.5 + 0.5) * edgeFade;
}, 3, 0.52);
}
// Data-dither canvases
document.querySelectorAll('[data-dither]').forEach(canvas => {
const type = canvas.dataset.dither;
if (type === 'glitch-wide') {
ditherCanvas(canvas, (x, y, cols, rows) =>
glitchNoise(x, y, cols, rows, 0, 0)
, 3, 0.42);
} else if (type === 'band') {
ditherCanvas(canvas, (x, y, cols, rows) => {
const nx = x / cols * 8;
const ny = y / rows * 3;
return fbm(nx, ny + 20, 3) * 0.5 + 0.5;
}, 3, 0.55);
} else if (type === 'smear') {
ditherCanvas(canvas, (x, y, cols, rows) =>
glitchNoise(x, y, cols, rows, 15, 7)
, 3, 0.45);
} else if (type === 'cloud') {
ditherCanvas(canvas, (x, y, cols, rows) => {
const nx = x / cols * 5;
const ny = y / rows * 3;
const n = fbm(nx + 40, ny + 10, 4);
const cx = (x / cols - 0.5) * 2;
const cy = (y / rows - 0.5) * 2;
const dist = Math.sqrt(cx * cx + cy * cy);
return (n * 0.5 + 0.5) * (1 - dist * 0.3);
}, 3, 0.5);
}
});
}
// Initial render
renderAllDither();
// Re-render on resize
let resizeTimer;
window.addEventListener('resize', () => {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(renderAllDither, 150);
});
})();
</script>
</body>
</html>