73 lines
No EOL
7.9 KiB
HTML
73 lines
No EOL
7.9 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Voronoi Pulse Fields — exploration</title>
|
|
<style>
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
body { background: #0a0a0f; color: #3a3a4a; font-family: ui-monospace, monospace; overflow: hidden; }
|
|
#grid { padding: 20px; font-size: 12px; line-height: 1.4; white-space: pre; }
|
|
.c { display: inline-block; transition: all 0.3s; }
|
|
.c.active { color: #fde68a; text-shadow: 0 0 6px rgba(253,230,138,0.5); }
|
|
.c.strong { color: #34d399; text-shadow: 0 0 10px rgba(52,211,153,0.6); font-weight: bold; }
|
|
.c.dead { color: #1a1a2a; }
|
|
#info { position: fixed; bottom: 10px; left: 20px; color: #3a3a4a; font-size: 11px; }
|
|
#controls { position: fixed; top: 10px; right: 20px; color: #666; font-size: 11px; }
|
|
#controls span { cursor: pointer; margin-left: 12px; color: #fde68a; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="grid"></div>
|
|
<div id="info">neurameba · physarum exploration</div>
|
|
<div id="controls"><span id="play-btn">play</span><span id="reset-btn">reset</span></div>
|
|
<script>
|
|
const text = "# About motd\n\n## What is motd?\n\nmotd is a terminal-aesthetic social platform. Commands in, text out. No chrome, no nav bars, no engagement bait. You type commands. You read text. That's it.\n\n## The Name\n\n`motd` — message of the day. The greeting a Unix server prints when you log in. On motd, the platform IS the message of the day. Every time you open it, the feed is what's happening now. Posts are ephemeral. The conversation is live.\n\n## Philosophy\n\n- **No vanity metrics.** No likes, no follower counts, no engagement scores. The feed is chronological, never algorithmic.\n- **Posts expire.** Active for 30 days, archived for 90, then permanently deleted. This isn't a ledger. It's a conversation.\n- **Discovery over curation.** Find things with `/find` and `/tree`. Not through an algorithm deciding what you should see.\n- **Moderation is silent.** `/kill` hides noise from your view. No drama, no public blocks, no announcements.\n- **API-first.** Every feature is an API endpoint. Build your own client, write a bot, pipe your feed into whatever you want.\n- **Text-first.** Media is supported (PNG, MP3, MP4) but text is the native format. Images get ASCII art previews in the feed.\n- **Three-column layout.** Feed in the center, docs and media open to the left, menu and help on the right. Like VS Code, but for social.\n\n## Who is it for?\n\nBuilders. Coders. Anyone who wants a social space that respects their attention.\n\nBuilt by [Anomata](https://anomata.fi).\n\n## Bridges\n\nConnect your account to Telegram or email. Receive bookmarked content on your phone or in your inbox. Message motd users from either. All posts are public.\n\n- `/read telegram` — Telegram bridge\n- `/read email` — email bridge\n\n## Read More\n\n- `/read commands` — full command reference\n- `/read api` — API documentation\n- `/read telegram` — Telegram bridge\n- `/read email` — email bridge\n- `/read plugins` — build on the API\n";
|
|
const passes = [{"t":0,"r":29,"c":0,"a":"died","s":0,"ps":8,"e":100,"pr":1},{"t":0,"r":39,"c":11,"a":"extend","s":0.4930545849555492,"ps":9,"e":71.81610567575106,"pr":1.1},{"t":0,"r":0,"c":4,"a":"extend","s":0.3215797598762352,"ps":9,"e":70.85584665530692,"pr":1.1},{"t":0,"r":7,"c":0,"a":"died","s":0,"ps":8,"e":100,"pr":1},{"t":1,"r":39,"c":11,"a":"extend","s":0.5274795052708354,"ps":8,"e":52.38515920254242,"pr":1.05},{"t":1,"r":0,"c":4,"a":"hold","s":0.29024343687905496,"ps":9,"e":71.82779415033936,"pr":1.1},{"t":1,"r":39,"c":10,"a":"hold","s":0.30370468681907586,"ps":5,"e":32.45796849844592,"pr":1.2000000000000002},{"t":1,"r":0,"c":5,"a":"extend","s":0.34110834220205244,"ps":5,"e":22.641960712923566,"pr":1.2000000000000002},{"t":2,"r":39,"c":11,"a":"extend","s":0.4930545849555492,"ps":7,"e":38.69571711753076,"pr":1},{"t":2,"r":0,"c":4,"a":"hold","s":0.2845685337719027,"ps":8,"e":72.90434242051458,"pr":1.05},{"t":2,"r":39,"c":10,"a":"hold","s":0.26473763594965255,"ps":5,"e":33.82586958604314,"pr":1.2000000000000002},{"t":2,"r":0,"c":5,"a":"hold","s":0.3560897117551941,"ps":4,"e":24.89067840696512,"pr":1.1500000000000001},{"t":2,"r":39,"c":12,"a":"hold","s":0.334497122769104,"ps":5,"e":24.376759497528155,"pr":1.1500000000000001},{"t":3,"r":39,"c":11,"a":"extend","s":0.48765286464571056,"ps":6,"e":29.18785802428751,"pr":0.95},{"t":3,"r":0,"c":4,"a":"retracted","s":0.3025728096306387,"ps":8,"e":74.1249248975597,"pr":1.05},{"t":3,"r":39,"c":10,"a":"retracted","s":0.26473763594965255,"ps":4,"e":35.343770673640364,"pr":1.1500000000000001},{"t":3,"r":0,"c":5,"a":"hold","s":0.3268391378597412,"ps":4,"e":26.90539150984305,"pr":1.1},{"t":3,"r":39,"c":12,"a":"hold","s":0.35026103580405155,"ps":5,"e":26.428847783960567,"pr":1.1500000000000001},{"t":3,"r":38,"c":11,"a":"hold","s":0.31315088221741383,"ps":5,"e":18.339085822395354,"pr":1.1},{"t":4,"r":39,"c":11,"a":"extend","s":0.4750309184700963,"ps":5,"e":22.566673760433794,"pr":0.8999999999999999},{"t":4,"r":0,"c":5,"a":"retracted","s":0.32014988447081616,"ps":4,"e":28.86659058560958,"pr":1.05},{"t":4,"r":39,"c":12,"a":"retracted","s":0.35026103580405155,"ps":4,"e":28.63093607039298,"pr":1.1},{"t":4,"r":38,"c":11,"a":"hold","s":0.31783362523930014,"ps":5,"e":20.131754824309755,"pr":1.1},{"t":5,"r":39,"c":11,"a":"extend","s":0.44044846936984056,"ps":4,"e":17.843183060774763,"pr":0.8499999999999999},{"t":5,"r":38,"c":11,"a":"retracted","s":0.31783362523930014,"ps":4,"e":22.074423826224155,"pr":1.05},{"t":6,"r":39,"c":11,"a":"hold","s":0.36658712646349295,"ps":4,"e":20.175880072482705,"pr":0.8499999999999999},{"t":6,"r":39,"c":12,"a":"hold","s":0.36876933127559897,"ps":5,"e":9.847233104822546,"pr":0.9499999999999998},{"t":7,"r":39,"c":11,"a":"hold","s":0.36658712646349295,"ps":4,"e":22.508577084190648,"pr":0.7999999999999998},{"t":7,"r":39,"c":12,"a":"hold","s":0.294979066003074,"ps":5,"e":11.457065632847138,"pr":0.9499999999999998},{"t":8,"r":39,"c":11,"a":"retracted","s":0.36658712646349295,"ps":4,"e":24.84127409589859,"pr":0.7499999999999998},{"t":8,"r":39,"c":12,"a":"retracted","s":0.294979066003074,"ps":4,"e":13.21689816087173,"pr":0.8999999999999998}];
|
|
const lines = text.split('\n');
|
|
const gridEl = document.getElementById('grid');
|
|
const charEls = [];
|
|
for (let r = 0; r < lines.length; r++) {
|
|
const row = [];
|
|
for (let c = 0; c < lines[r].length; c++) {
|
|
const s = document.createElement('span');
|
|
s.className = 'c';
|
|
s.textContent = lines[r][c];
|
|
row.push(s);
|
|
gridEl.appendChild(s);
|
|
}
|
|
charEls.push(row);
|
|
gridEl.appendChild(document.createTextNode('\n'));
|
|
}
|
|
let tick = -1, playing = false, iv;
|
|
function apply(t) {
|
|
for (const r of charEls) for (const e of r) e.className = 'c';
|
|
const active = new Map();
|
|
for (const p of passes) {
|
|
if (p.t > t) break;
|
|
const k = p.r+','+p.c;
|
|
if (p.a === 'died' || p.a === 'retracted') active.set(k, 'dead');
|
|
else if (p.ps > 16) active.set(k, 'strong');
|
|
else active.set(k, 'active');
|
|
}
|
|
for (const [k, cls] of active) {
|
|
const [r, c] = k.split(',').map(Number);
|
|
if (charEls[r]?.[c]) charEls[r][c].className = 'c ' + cls;
|
|
}
|
|
document.getElementById('info').textContent = 'tick ' + t + ' · ' + [...active.values()].filter(v=>v!=='dead').length + ' alive';
|
|
}
|
|
function play() {
|
|
if (playing) return;
|
|
playing = true;
|
|
document.getElementById('play-btn').textContent = 'pause';
|
|
const max = passes.length > 0 ? passes[passes.length-1].t : 0;
|
|
iv = setInterval(() => { tick++; if (tick > max) { pause(); return; } apply(tick); }, 900);
|
|
}
|
|
function pause() { playing = false; clearInterval(iv); document.getElementById('play-btn').textContent = 'play'; }
|
|
function reset() { pause(); tick = -1; for (const r of charEls) for (const e of r) e.className = 'c'; document.getElementById('info').textContent = 'neurameba'; }
|
|
document.getElementById('play-btn').addEventListener('click', () => playing ? pause() : play());
|
|
document.getElementById('reset-btn').addEventListener('click', reset);
|
|
setTimeout(play, 1000);
|
|
</script>
|
|
</body>
|
|
</html> |