Day 20,834 – seed 570015014144

Today turns a page.

The Year of the Fire Horse arrives with a kind of restless energy. Horse years are said to carry motion, independence, forward momentum. Add fire to that, and it feels less like a quiet candle and more like a forge. Heat that shapes. Heat that transforms.

There is something fitting about that thought. A horse does not idle for long. It moves. It tests fences. It runs because running is what it was built to do. Fire does not apologize for being bright. It simply burns.

I like the symbolism of beginning again under that banner. Not a timid start. Not a cautious shuffle into the months ahead. But a year that invites courage. Initiative. Maybe even a little boldness.

The lunar new year always feels different from January 1. Less about resolutions and more about rhythm. Cycles. A reminder that time does not just march forward in straight lines. It circles back, renews itself, offers another chance to step differently into familiar terrain.

So here is to the Fire Horse. To motion after stillness. To warmth in cold places. To the quiet decision to run toward what matters.

May this year carry strength without recklessness, passion without burnout, and forward motion without losing sight of where we started.

#doodle #lunarnewyear2026 #yearofthefirehorse

Hour 500,005. Never thought I’d make it.

Nice palindrome,  if nothing else.

Day 20,834, seed 570014142729

Today moved quietly.

Not in a sleepy way. Just steady. Like it knew exactly how much it needed to be and refused to be any more than that.

Somewhere along the way, the calendar and the clock quietly noted a small personal milestone: passing the 500,005th hour. No fanfare, no fireworks, just another tick forward in the long, ongoing accumulation of days, which somehow made it feel even more meaningful.

The morning light came in soft and undecided, the kind that does not commit to drama. No grand sunrise performance. Just a gradual brightening of corners. Coffee tasted like coffee. Floors creaked in familiar places. The small rituals held their shape.

Later on, the grill came to life and the in-laws stopped by, the backyard filling with that familiar mixture of conversation, laughter, and the steady sound of food cooking over open heat. Veggie burgers made their way onto plates, simple and good, the kind of meal that works best when nobody is in a hurry and everyone stays just a little longer than planned.

Outside, the air had that in-between feeling. Not quite winter, not quite spring. The trees stood patient. The birds seemed to be negotiating something among themselves. Even the wind felt measured, as if it had agreed not to make a scene.

Nothing dramatic happened. No sudden revelations. No plot twists.

And that was the gift of it.

Some days arrive like a thunderclap. Others just sit down beside you and keep you company. Today was the second kind. The kind that asks nothing more than attention. Sometimes that is enough.

Day 20,831 Webs doodler update – 570012094830

Web drawing now has wind and a few color mods

https://svonberg.org/wp-content/uploads/2026/02/webs2.html


<!DOCTYPE html>
<html lang=”en”>
<head>
    <meta charset=”UTF-8″>
    <meta name=”viewport” content=”width=device-width, initial-scale=1.0″>
    <title>Zen Spiderweb Generator</title>
    <script src=”https://cdn.tailwindcss.com”></script>
    <style>
        body, html {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
            overflow: hidden;
            background-color: #111827;
            font-family: ‘Segoe UI’, Tahoma, Geneva, Verdana, sans-serif;
            transition: background 1s ease;
        }

        #canvas-container {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            z-index: 0;
        }

        canvas {
            display: block;
        }

        /* Custom Scrollbar */
        .custom-scroll::-webkit-scrollbar {
            width: 6px;
        }
        .custom-scroll::-webkit-scrollbar-track {
            background: rgba(255, 255, 255, 0.05);
        }
        .custom-scroll::-webkit-scrollbar-thumb {
            background: rgba(255, 255, 255, 0.2);
            border-radius: 3px;
        }
        .custom-scroll::-webkit-scrollbar-thumb:hover {
            background: rgba(255, 255, 255, 0.4);
        }

        /* Range Slider Styling */
        input[type=range] {
            -webkit-appearance: none;
            width: 100%;
            background: transparent;
        }
        input[type=range]::-webkit-slider-thumb {
            -webkit-appearance: none;
            height: 16px;
            width: 16px;
            border-radius: 50%;
            background: #e5e7eb;
            cursor: pointer;
            margin-top: -6px;
            box-shadow: 0 0 5px rgba(0,0,0,0.5);
            transition: transform 0.1s;
        }
        input[type=range]::-webkit-slider-thumb:hover {
            transform: scale(1.1);
        }
        input[type=range]::-webkit-slider-runnable-track {
            width: 100%;
            height: 4px;
            cursor: pointer;
            background: rgba(255, 255, 255, 0.2);
            border-radius: 2px;
        }
       
        /* Section dividers */
        .control-group {
            border-top: 1px solid rgba(255,255,255,0.1);
            padding-top: 1rem;
            margin-top: 1rem;
        }

        /* Zen Button Specifics */
        #open-btn {
            z-index: 50; /* Ensure it is above everything */
            transition: all 0.3s ease;
            box-shadow: 0 0 15px rgba(0,0,0,0.5);
        }
        #open-btn:hover {
            transform: scale(1.1) rotate(90deg);
        }
    </style>
</head>
<body>

    <!– Canvas Layer –>
    <div id=”canvas-container”>
        <canvas id=”webCanvas”></canvas>
    </div>

    <!– UI Overlay –>
    <div id=”ui-layer” class=”absolute top-0 right-0 p-4 md:p-6 z-10 w-full md:w-[400px] h-full pointer-events-none transition-transform duration-500 ease-in-out transform translate-x-0″>
       
        <!– Main Controls Panel –>
        <div id=”controls-panel” class=”bg-gray-900/90 backdrop-blur-md border border-gray-700/50 rounded-2xl p-5 shadow-2xl text-gray-200 custom-scroll h-full max-h-full overflow-y-auto pointer-events-auto flex flex-col”>
           
            <div class=”flex justify-between items-center mb-4 flex-shrink-0″>
                <div>
                    <h1 class=”text-xl font-light tracking-wider text-white”>Silk Weaver</h1>
                    <p class=”text-xs text-gray-400″>Procedural Generator v2.1</p>
                </div>
                <button id=”close-btn” class=”p-2 text-gray-300 hover:text-white transition-colors bg-white/10 hover:bg-white/20 rounded-lg flex items-center gap-2″ title=”Enter Zen Mode”>
                    <span class=”text-xs font-medium uppercase tracking-wider”>Zen Mode</span>
                    <svg xmlns=”http://www.w3.org/2000/svg” width=”18″ height=”18″ viewBox=”0 0 24 24″ fill=”none” stroke=”currentColor” stroke-width=”2″ stroke-linecap=”round” stroke-linejoin=”round”><path d=”M15 3h6v6M14 10l6.1-6.1M9 21H3v-6M10 14l-6.1 6.1″/></svg>
                </button>
            </div>

            <div class=”space-y-4 flex-grow”>
               
                <!– THEME SELECTOR –>
                <div>
                    <label class=”text-xs uppercase tracking-widest text-indigo-400 font-semibold mb-2 block”>Atmosphere</label>
                    <div class=”grid grid-cols-4 gap-2″>
                        <button class=”theme-btn bg-gray-800 border-2 border-indigo-500 rounded h-8 w-full hover:brightness-110 transition-all” data-theme=”midnight” title=”Midnight”></button>
                        <button class=”theme-btn bg-green-900 border-2 border-transparent hover:border-gray-400 rounded h-8 w-full transition-all” data-theme=”forest” title=”Forest”></button>
                        <button class=”theme-btn bg-purple-900 border-2 border-transparent hover:border-gray-400 rounded h-8 w-full transition-all” data-theme=”sunset” title=”Sunset”></button>
                        <button class=”theme-btn bg-black border-2 border-transparent hover:border-gray-400 rounded h-8 w-full transition-all” data-theme=”cyber” title=”Cyber”></button>
                    </div>
                </div>

                <!– CORE SHAPE –>
                <div class=”control-group”>
                    <label class=”text-xs uppercase tracking-widest text-indigo-400 font-semibold mb-3 block”>Structure</label>
                   
                    <div class=”space-y-4″>
                        <div>
                            <div class=”flex justify-between text-xs text-gray-400 mb-1″>
                                <span>Radial Spokes</span>
                                <span id=”val-density”>12</span>
                            </div>
                            <input type=”range” id=”density” min=”5″ max=”30″ value=”12″ step=”1″>
                        </div>

                        <div>
                            <div class=”flex justify-between text-xs text-gray-400 mb-1″>
                                <span>Spiral Spacing</span>
                                <span id=”val-spacing”>20</span>
                            </div>
                            <input type=”range” id=”spacing” min=”10″ max=”60″ value=”20″ step=”5″>
                        </div>
                       
                        <div>
                            <div class=”flex justify-between text-xs text-gray-400 mb-1″>
                                <span>Center Size</span>
                                <span id=”val-centerSize”>50</span>
                            </div>
                            <input type=”range” id=”centerSize” min=”0″ max=”200″ value=”50″ step=”10″>
                        </div>
                    </div>
                </div>

                <!– PHYSICS & CHAOS –>
                <div class=”control-group”>
                    <label class=”text-xs uppercase tracking-widest text-indigo-400 font-semibold mb-3 block”>Physics & Chaos</label>
                   
                    <div class=”space-y-4″>
                        <div>
                            <div class=”flex justify-between text-xs text-gray-400 mb-1″>
                                <span>Thread Slack (Gravity)</span>
                                <span id=”val-slack”>0.5</span>
                            </div>
                            <input type=”range” id=”slack” min=”0″ max=”1″ value=”0.5″ step=”0.1″>
                        </div>

                        <div>
                            <div class=”flex justify-between text-xs text-gray-400 mb-1″>
                                <span>Tear Probability</span>
                                <span id=”val-tears”>0%</span>
                            </div>
                            <input type=”range” id=”tears” min=”0″ max=”0.4″ value=”0″ step=”0.05″>
                        </div>

                         <div>
                            <div class=”flex justify-between text-xs text-gray-400 mb-1″>
                                <span>Irregularity</span>
                                <span id=”val-chaos”>0.3</span>
                            </div>
                            <input type=”range” id=”chaos” min=”0″ max=”1″ value=”0.3″ step=”0.1″>
                        </div>
                    </div>
                </div>

                <!– VISUALS –>
                <div class=”control-group”>
                    <label class=”text-xs uppercase tracking-widest text-indigo-400 font-semibold mb-3 block”>Visual FX</label>
                   
                    <div class=”space-y-4″>
                         <div>
                            <div class=”flex justify-between text-xs text-gray-400 mb-1″>
                                <span>Glow Strength</span>
                                <span id=”val-glow”>Low</span>
                            </div>
                            <input type=”range” id=”glow” min=”0″ max=”20″ value=”0″ step=”1″>
                        </div>

                        <div>
                            <div class=”flex justify-between text-xs text-gray-400 mb-1″>
                                <span>Wind Speed</span>
                                <span id=”val-wind”>0</span>
                            </div>
                            <input type=”range” id=”wind” min=”0″ max=”5″ value=”0″ step=”0.5″>
                        </div>

                        <div class=”flex items-center justify-between”>
                            <span class=”text-sm text-gray-300″>Morning Dew</span>
                            <label class=”relative inline-flex items-center cursor-pointer”>
                                <input type=”checkbox” id=”dew-toggle” class=”sr-only peer”>
                                <div class=”w-9 h-5 bg-gray-700 peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[”] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-4 after:w-4 after:transition-all peer-checked:bg-indigo-500″></div>
                            </label>
                        </div>
                    </div>
                </div>

                <!– SYSTEM –>
                <div class=”control-group”>
                    <div class=”flex items-center justify-between”>
                        <span class=”text-sm text-gray-300″>Instant Draw</span>
                        <label class=”relative inline-flex items-center cursor-pointer”>
                            <input type=”checkbox” id=”instant-toggle” class=”sr-only peer”>
                            <div class=”w-9 h-5 bg-gray-700 peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[”] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-4 after:w-4 after:transition-all peer-checked:bg-indigo-500″></div>
                        </label>
                    </div>
                </div>

            </div>

            <!– Actions –>
            <div class=”mt-6 grid grid-cols-2 gap-3 flex-shrink-0″>
                <button id=”generate-btn” class=”col-span-2 bg-indigo-600 hover:bg-indigo-500 text-white font-medium py-3 px-4 rounded-xl transition-all shadow-lg shadow-indigo-500/20 active:scale-95 flex justify-center items-center gap-2″>
                    <svg xmlns=”http://www.w3.org/2000/svg” width=”16″ height=”16″ viewBox=”0 0 24 24″ fill=”none” stroke=”currentColor” stroke-width=”2″ stroke-linecap=”round” stroke-linejoin=”round”><path d=”M21 12a9 9 0 1 1-6.219-8.56″/></svg>
                    Re-Weave
                </button>
                <button id=”download-btn” class=”bg-gray-800 hover:bg-gray-700 text-gray-300 text-sm py-2 px-3 rounded-lg transition-colors border border-gray-700″>
                    Save Img
                </button>
                 <button id=”clear-btn” class=”bg-gray-800 hover:bg-red-900/30 text-gray-300 hover:text-red-200 text-sm py-2 px-3 rounded-lg transition-colors border border-gray-700″>
                    Clear
                </button>
            </div>
        </div>
    </div>

    <!– Zen Mode Restore Button (Fixed Visibility) –>
    <button id=”open-btn” class=”fixed bottom-6 right-6 bg-white/10 hover:bg-white/20 text-white p-4 rounded-full backdrop-blur-md border border-white/30 hidden transition-all” title=”Exit Zen Mode”>
        <svg xmlns=”http://www.w3.org/2000/svg” width=”28″ height=”28″ viewBox=”0 0 24 24″ fill=”none” stroke=”currentColor” stroke-width=”2″ stroke-linecap=”round” stroke-linejoin=”round”><circle cx=”12″ cy=”12″ r=”3″></circle><path d=”M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z”></path></svg>
    </button>

    <script>
        /**
         * Core Logic for Spiderweb Generator
         */
        const canvas = document.getElementById(‘webCanvas’);
        const ctx = canvas.getContext(‘2d’);
       
        // State
        let width, height;
        let centerX, centerY;
        let animationFrameId;
        let time = 0; // For wind animation
       
        // Web Data
        let spokes = [];
        let spirals = [];
        let currentDrawIndex = 0;
        let isConstructing = false; // Is the initial build animation running?

        // Theme Configuration
        const themes = {
            midnight: { bg: ‘#111827’, web: ‘rgba(255, 255, 255, 0.2)’, dew: ‘rgba(255, 255, 255, 0.6)’ },
            forest:   { bg: ‘#064e3b’, web: ‘rgba(209, 250, 229, 0.2)’, dew: ‘rgba(167, 243, 208, 0.6)’ },
            sunset:   { bg: ‘linear-gradient(to bottom, #4c1d95, #c2410c)’, web: ‘rgba(254, 215, 170, 0.3)’, dew: ‘rgba(255, 237, 213, 0.7)’ },
            cyber:    { bg: ‘#000000’, web: ‘rgba(50, 255, 100, 0.3)’, dew: ‘rgba(50, 255, 100, 0.8)’ }
        };

        // Settings
        const settings = {
            theme: ‘midnight’,
            spokeCount: 12,
            spacing: 20, // Distance between spiral loops
            centerSize: 50,
            slack: 0.5,
            chaos: 0.3,
            tears: 0.0,
            glow: 0,
            wind: 0.0,
            speed: 15,
            dew: false,
            instant: false
        };

        // UI Elements
        const uiLayer = document.getElementById(‘ui-layer’);
        const openBtn = document.getElementById(‘open-btn’);
        const themeBtns = document.querySelectorAll(‘.theme-btn’);

        // Helper: Random Range
        const random = (min, max) => Math.random() * (max – min) + min;

        // Helper: Apply Wind to a Point
        // Returns a new object so we don’t mutate original geometry permanently
        function applyWind(point) {
            if (settings.wind <= 0.1) return point;
           
            // Simple vertex displacement based on time and Y position
            // Stronger effect further from center?
            const dist = Math.sqrt((point.x – centerX)**2 + (point.y – centerY)**2);
            const distFactor = Math.min(dist / 500, 1);

            const offsetX = Math.sin(time * 0.002 + point.y * 0.01) * (settings.wind * 10) * distFactor;
            const offsetY = Math.cos(time * 0.003 + point.x * 0.01) * (settings.wind * 5) * distFactor;

            return {
                x: point.x + offsetX,
                y: point.y + offsetY
            };
        }

        // Initialization
        function resize() {
            width = window.innerWidth;
            height = window.innerHeight;
            canvas.width = width;
            canvas.height = height;
            centerX = width / 2;
            centerY = height / 2;
            generateWebData();
            startDrawing();
        }

        window.addEventListener(‘resize’, resize);

        // Generate the mathematical model of the web
        function generateWebData() {
            spokes = [];
            spirals = [];
           
            // 1. Generate Spokes (Radials)
            const baseAngle = (Math.PI * 2) / settings.spokeCount;
            const maxRadius = Math.max(width, height) * 0.8;

            for (let i = 0; i < settings.spokeCount; i++) {
                let angleOffset = (Math.random() – 0.5) * settings.chaos;
                let angle = (i * baseAngle) + angleOffset;
                let length = maxRadius * random(0.8, 1.2);

                spokes.push({
                    angle: angle,
                    length: length,
                    endX: centerX + Math.cos(angle) * length,
                    endY: centerY + Math.sin(angle) * length
                });
            }

            // 2. Generate Spiral Segments
            let currentRadius = settings.centerSize;
            let spokeIndex = 0;
           
            // Prevent infinite loops
            let safetyCount = 0;
           
            while (safetyCount < 5000) {
                safetyCount++;
               
                let s1 = spokes[spokeIndex];
                let nextIndex = (spokeIndex + 1) % spokes.length;
                let s2 = spokes[nextIndex];

                // R1 and R2 allow the spiral to spiral outwards
                let r1 = currentRadius + random(-5, 5) * (settings.chaos * 5);
                // Look ahead: The connection point on the next spoke is slightly further out
                let spiralStep = (settings.spacing / settings.spokeCount);
                let r2 = r1 + spiralStep + random(-2, 2) * (settings.chaos * 5);
               
                // Stop if off screen
                if (r1 > s1.length || r2 > s2.length) break;

                // Probability to skip a segment (Tear)
                if (Math.random() > settings.tears) {

                    let p1 = {
                        x: centerX + Math.cos(s1.angle) * r1,
                        y: centerY + Math.sin(s1.angle) * r1
                    };

                    let p2 = {
                        x: centerX + Math.cos(s2.angle) * r2,
                        y: centerY + Math.sin(s2.angle) * r2
                    };

                    // Control Point for Catenary (gravity sag)
                    let midX = (p1.x + p2.x) / 2;
                    let midY = (p1.y + p2.y) / 2;
                   
                    // Pull vector towards center
                    let dirX = midX – centerX;
                    let dirY = midY – centerY;
                    let pullFactor = settings.slack * 0.4;
                   
                    let cpX = midX – (dirX * pullFactor);
                    let cpY = midY – (dirY * pullFactor);

                    spirals.push({
                        p1: p1,
                        p2: p2,
                        cp: {x: cpX, y: cpY},
                        dew: Math.random() > 0.7
                    });
                }

                // Advance
                spokeIndex = nextIndex;
                currentRadius += spiralStep; // Increment radius constantly around the spiral
               
                if (currentRadius > Math.max(width, height) * 0.7) break;
            }
        }

        function getThemeColors() {
            return themes[settings.theme] || themes[‘midnight’];
        }

        function drawDew(p1, p2, count) {
            const theme = getThemeColors();
            ctx.fillStyle = theme.dew;
           
            for(let i=1; i<count; i++) {
                const t = i/count;
                const x = p1.x + (p2.x – p1.x) * t;
                const y = p1.y + (p2.y – p1.y) * t;
               
                ctx.beginPath();
                ctx.arc(x, y, random(0.5, 1.5), 0, Math.PI * 2);
                ctx.fill();
            }
        }

        function drawDewOnCurve(p1, cp, p2) {
             const theme = getThemeColors();
             ctx.fillStyle = theme.dew;

             const drops = Math.floor(random(1, 4));
             for (let i = 0; i < drops; i++) {
                const t = random(0.2, 0.8);
                // Bezier Point
                const x = (1 – t) * (1 – t) * p1.x + 2 * (1 – t) * t * cp.x + t * t * p2.x;
                const y = (1 – t) * (1 – t) * p1.y + 2 * (1 – t) * t * cp.y + t * t * p2.y;

                ctx.beginPath();
                ctx.arc(x, y, random(0.5, 1.8), 0, Math.PI * 2);
                ctx.fill();
             }
        }

        function startDrawing() {
            if (isConstructing) isConstructing = false; // Reset current build
            currentDrawIndex = 0;
           
            // Set Background based on theme
            const theme = getThemeColors();
            if (theme.bg.includes(‘gradient’)) {
                document.body.style.background = theme.bg;
            } else {
                document.body.style.backgroundColor = theme.bg;
                document.body.style.backgroundImage = ‘none’;
            }

            if (settings.instant) {
                currentDrawIndex = spirals.length;
            } else {
                isConstructing = true;
            }
           
            if (!animationFrameId) animate();
        }

        function renderFrame() {
            ctx.clearRect(0, 0, width, height);
            const theme = getThemeColors();
           
            // Setup Glow
            if (settings.glow > 0) {
                ctx.shadowBlur = settings.glow;
                ctx.shadowColor = theme.web;
            } else {
                ctx.shadowBlur = 0;
            }

            // 1. Draw Spokes
            ctx.strokeStyle = theme.web;
            ctx.lineWidth = 1;
            ctx.lineCap = “round”;
           
            // Only draw spokes if we are generating or they are always visible
            spokes.forEach(spoke => {
                const start = applyWind({x: centerX, y: centerY});
                const end = applyWind({x: spoke.endX, y: spoke.endY});

                ctx.beginPath();
                ctx.moveTo(start.x, start.y);
                ctx.lineTo(end.x, end.y);
                ctx.stroke();
            });

            // 2. Draw Spirals
            // If constructing, limit by currentDrawIndex. If done, draw all.
            const limit = isConstructing ? currentDrawIndex : spirals.length;
           
            // Batch drawing for performance? Canvas handles single paths better
            // But we need individual segments for wind
            ctx.strokeStyle = theme.web; // Re-apply incase changed
            ctx.lineWidth = Math.max(0.5, 0.8 – (settings.spokeCount * 0.01)); // Thinner webs for high density

            ctx.beginPath();
            for(let i=0; i < limit; i++) {
                let seg = spirals[i];
               
                // Apply wind to all control points
                let p1 = applyWind(seg.p1);
                let p2 = applyWind(seg.p2);
                let cp = applyWind(seg.cp);

                ctx.moveTo(p1.x, p1.y);
                ctx.quadraticCurveTo(cp.x, cp.y, p2.x, p2.y);
            }
            ctx.stroke();

            // 3. Draw Dew (Separate pass for color change)
            if (settings.dew) {
                ctx.shadowBlur = 0; // No glow on dew for crispness
                for(let i=0; i < limit; i++) {
                    let seg = spirals[i];
                    if (seg.dew) {
                        // Recalculate positions for dew to match wind
                        let p1 = applyWind(seg.p1);
                        let p2 = applyWind(seg.p2);
                        let cp = applyWind(seg.cp);
                        drawDewOnCurve(p1, cp, p2);
                    }
                }
            }

            // Logic for Construction Animation
            if (isConstructing) {
                let speed = Math.floor(settings.speed + (currentDrawIndex / 50));
                currentDrawIndex += speed;
                if (currentDrawIndex >= spirals.length) {
                    currentDrawIndex = spirals.length;
                    isConstructing = false;
                }
            }
        }

        function animate(timestamp) {
            time = timestamp || 0;
            renderFrame();
            animationFrameId = requestAnimationFrame(animate);
        }

        // UI Interactions
        function updateDisplay(id, val) {
            const el = document.getElementById(`val-${id}`);
            if (el) el.innerText = val;
        }

        // Attach listeners to all range inputs
        [‘density’, ‘spacing’, ‘centerSize’, ‘slack’, ‘chaos’, ‘tears’, ‘glow’, ‘wind’].forEach(id => {
            const input = document.getElementById(id);
            input.addEventListener(‘input’, (e) => {
                let val = parseFloat(e.target.value);
               
                // Special formatting
                if (id === ‘tears’) updateDisplay(id, Math.round(val * 100) + ‘%’);
                else if (id === ‘glow’) updateDisplay(id, val === 0 ? ‘Off’ : (val > 15 ? ‘High’ : ‘Low’));
                else updateDisplay(id, val);

                // Map to settings
                if (id === ‘density’) settings.spokeCount = parseInt(val);
                else if (id === ‘spacing’) settings.spacing = parseInt(val);
                else if (id === ‘centerSize’) settings.centerSize = parseInt(val);
                else settings[id] = val;

                // Regen logic
                if ([‘density’, ‘spacing’, ‘centerSize’, ‘chaos’, ‘tears’, ‘slack’].includes(id)) {
                    generateWebData();
                    if (!settings.instant && !isConstructing) startDrawing(); // Restart anim if structural change
                }
            });
        });

        // Theme Buttons
        themeBtns.forEach(btn => {
            btn.addEventListener(‘click’, (e) => {
                themeBtns.forEach(b => b.classList.remove(‘border-indigo-500’));
                themeBtns.forEach(b => b.classList.add(‘border-transparent’));
               
                e.target.classList.remove(‘border-transparent’);
                e.target.classList.add(‘border-indigo-500’);

                settings.theme = e.target.getAttribute(‘data-theme’);
                startDrawing();
            });
        });

        document.getElementById(‘dew-toggle’).addEventListener(‘change’, (e) => {
            settings.dew = e.target.checked;
        });

        document.getElementById(‘instant-toggle’).addEventListener(‘change’, (e) => {
            settings.instant = e.target.checked;
        });

        document.getElementById(‘generate-btn’).addEventListener(‘click’, () => {
            generateWebData();
            startDrawing();
        });
       
        document.getElementById(‘clear-btn’).addEventListener(‘click’, () => {
             isConstructing = false;
             currentDrawIndex = 0;
             spokes = [];
             spirals = [];
             ctx.clearRect(0,0,width,height);
        });

        document.getElementById(‘download-btn’).addEventListener(‘click’, () => {
            const link = document.createElement(‘a’);
            link.download = `spiderweb_${settings.theme}.png`;
            link.href = canvas.toDataURL();
            link.click();
        });

        // Zen Mode Logic
        const closeBtn = document.getElementById(‘close-btn’);
        let uiVisible = true;

        function toggleUI() {
            uiVisible = !uiVisible;
            if (uiVisible) {
                uiLayer.classList.remove(‘translate-x-full’);
                openBtn.classList.add(‘hidden’);
            } else {
                uiLayer.classList.add(‘translate-x-full’);
                // Remove hidden immediately, CSS transitions handle the rest if you want opacity,
                // but for now simple display toggling ensures it’s clickable.
                openBtn.classList.remove(‘hidden’);
            }
        }

        closeBtn.addEventListener(‘click’, toggleUI);
        openBtn.addEventListener(‘click’, toggleUI);
       
        document.addEventListener(‘click’, (e) => {
            // If Zen mode is active (UI hidden) and we aren’t clicking the restore button
            if (!uiVisible && !openBtn.contains(e.target)) {
                // Gentle regen in Zen mode
                generateWebData();
                startDrawing();
            }
        });

        // Start
        resize();
        animate();

    </script>
</body>
</html>

Via yani

I messaged myself someone else’s Epstein story and IG put me on restriction. I can still post and even share stories, but I’m not allowed to message! Yeah, *I’M* the problem! So if you messaged me, I can’t respond for 3 days. Thanks IG for protecting PDFs!

“The Hangman” from 1964 based on the poem by Maurice Ogden. Film made by Les Goldman and Paul Julian. “The 1964 animated short film Hangman, directed by Paul Julian and Les Goldman, is a 16.4K cautionary tale about apathy and complicity, based on Maurice Ogden’s 1951 poem. It depicts a mysterious figure erecting a gallows in a town square, systematically executing citizens while the fearful populace: remains silent, only for the narrator to realize he is next.”

The ending of "The Hangman" from 1964 based on the poem by Maurice Ogden. Film made by Les Goldman and Paul Julian.

Scottobear (@scottobear.bsky.social) 2026-02-12T09:48:05.228Z

Putting the wait in perspective

There’s a specific kind of internal hum that starts the moment you hit “Checkout” on a piece of niche tech. It’s been a minute since I’ve been this genuinely keyed up for a mail call, but the wait for the x4 has me checking tracking tabs like it’s a competitive sport.

To keep my sanity, I did what any self-respecting nerd does: I opened a spreadsheet.

Putting the wait into perspective helps dampen the “where is it?” anxiety. My device left Shenzhen nine days ago. Since then, it’s been navigating a gauntlet of weather patterns, logistics hubs, and handoff hops.

Here is the breakdown of its 7,842-mile trek:

* Distance Covered: 7,105 miles (roughly 90% of the trip).
* Average Speed: A steady 36 mph.
* Touchpoints: 8 distinct scans across the globe.

It’s currently cleared the customs hurdle, and with the bulk of the journey in the rearview mirror, I’m optimistic for a delivery before the 14th.

Some people have that “buy it and forget it” zen mindset. I am not those people. I can’t just let it arrive “whenever.” To bridge the gap, I’ve been curate-stacking my digital environment:

* Customizing the Vibe: Designing a fresh set of wallpapers.
* Community Building: Getting the account settled over at readme.club.
* The Library: Hoarding choice EPUB files like a digital dragon.

I’m also diving back into Calibre. I haven’t touched the software in nearly a decade, but seeing that it’s still the gold standard for library management is a testament to its staying power. Getting a Calibre server spun up is next on the weekend warrior list.

The SD card is “ready-ish,” and the Android apps are already staged on my phone and tablet. Software-wise, I’m leaning toward Crosspoint. If I can swing a multi-boot setup to test the waters of every OS flavor available, that’s the dream.

Day 20826 – 570007063317

Woolgathering: What most people call their “self” is, for the most part, a bundle of different moods, impulses, and roles fighting for control. “I decided” usually means “one impulse temporarily won.”

If this sounds preposterous, spend some time in honest “self-observation”. I’m betting you find a constellation of “selves” rather a self.

The “self” you experience when hearing a nostalgic song, for example, can be very different from the “self” that you experience after stubbing your toe or getting cut off in traffic. Often dramatically different.

In the episode “Troubled Waters,” the good Lieutenant seems to pass some gas, and it can clearly be heard. After Columbo first examines the crime scene, he heads to the doctor’s office to get help for his seasickness. Just as he reaches the top of the steps, just before he reaches the doctor’s door, you can clearly hear two farts. He even sighs after the first one. Now, I thought it might have been the floor creaking or the ship settling, but that particular sound is never heard again in the episode. So, say what you will, but I’m convinced it’s a fart, and the sound guy missed it… or perhaps they left it in. After all, he was experiencing problems with his tummy.

Welcome to my wall scrawls.