{"id":4941,"date":"2026-01-10T00:09:47","date_gmt":"2026-01-10T00:09:47","guid":{"rendered":"https:\/\/mina.systems\/?page_id=4941"},"modified":"2026-01-10T14:38:04","modified_gmt":"2026-01-10T14:38:04","slug":"fractal_explorer","status":"publish","type":"page","link":"https:\/\/mina.systems\/ja\/fractal_explorer\/","title":{"rendered":"Fractal Explorer"},"content":{"rendered":"\n<style>\n    body, html { margin: 0; padding: 0; width: 100vw; height: 100vh; overflow: hidden; background: #000; }\n    #fractal { position: absolute; inset: 0; z-index: 1; }\n    #fractal canvas { display: block; width: 100%; height: 100%; }\n    \n    .hud { position: absolute; bottom: 20px; color: rgba(255,255,255,0.7); font-family: monospace; background: rgba(0,0,0,0.6); padding: 12px; border: 1px solid rgba(255,255,255,0.1); z-index: 10; transition: all 0.3s; opacity: 1; }\n    #hud-left{\n    font-family: 'Courier New', monospace; \/* Monospace keeps digits aligned *\/\n    font-size: 10px;                       \/* Smaller size to fit ~65 characters *\/\n    line-height: 1.2;\n    letter-spacing: -0.2px;                \/* Tightens text for narrow screens *\/\n    pointer-events: auto;                  \/* Ensures click-to-copy still works *\/\n    background: rgba(0, 0, 0, 0.3);        \/* Slight backdrop for readability *\/\n    padding: 5px;\n    border-radius: 4px;\n}\n#hud-right{\n    position: absolute;\n    right: 20px;\n    font-family: 'Courier New', monospace; \/* Monospace keeps digits aligned *\/\n    font-size: 10px;                       \/* Smaller size to fit ~65 characters *\/\n    line-height: 1.2;\n    letter-spacing: -0.2px;                \/* Tightens text for narrow screens *\/\n    pointer-events: auto;                  \/* Ensures click-to-copy still works *\/\n    background: rgba(0, 0, 0, 0.3);        \/* Slight backdrop for readability *\/\n    padding: 5px;\n    border-radius: 4px;\n}\n\n    #notify { position: absolute; top: 100px; left: 50%; transform: translateX(-50%); color: #fff; font-family: monospace; opacity: 0; transition: opacity 0.5s; pointer-events: none; font-size: 24px; text-shadow: 0 0 10px #000; z-index: 20; font-weight: bold; }\n\n    #tutorial-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.3); display: flex; justify-content: center; align-items: center; z-index: 100; pointer-events: none; }\n    #tutorial-text { color: white; font-family: monospace; font-size: 20px; text-align: center; max-width: 600px; line-height: 1.6; text-shadow: 0 0 10px #000; background: rgba(0,0,0,0.25); padding: 30px; border-radius: 25px; pointer-events: all; cursor: pointer; user-select: none; }\n    footer{display: none;}\n<\/style>\n\n<div id=\"fractal\"><\/div>\n<div id=\"notify\">SAVED<\/div>\n<div id=\"hud-right\" class=\"hud\">THEME: ACID<br>ZOOM: 1.00e+0<\/div>\n<div id=\"hud-left\" class=\"hud\">RE: -7.50e-1<br>IM: 0.00e+0<\/div>\n\n<div id=\"tutorial-overlay\">\n    <div id=\"tutorial-text\">\n        FRACTAL EXPLORER<br>*PHOTOSENSITIVITY WARNING*<br>\n        [CLICK to Begin Walkthrough]<br>\n        [ESC to Exit]\n    <\/div>\n<\/div>\n\n<script type=\"module\">\n(async () => {\n    const container = document.getElementById(\"fractal\"), \n          hudR = document.getElementById(\"hud-right\"), \n          hudL = document.getElementById(\"hud-left\"), \n          notify = document.getElementById(\"notify\"),\n          tutText = document.getElementById(\"tutorial-text\"), \n          tutOverlay = document.getElementById(\"tutorial-overlay\");\n    \n    const canvas = document.createElement(\"canvas\");\n    container.appendChild(canvas);\n\n    const adapter = await navigator.gpu.requestAdapter(), \n          device = await adapter.requestDevice(), \n          context = canvas.getContext(\"webgpu\");\n    \n    const dpr = window.devicePixelRatio || 1, \n          SCALE_BITS = 10n ** 60n;\n    \n    \/\/ 1. Define Palettes FIRST so they can be referenced below\n    const palettes = [\n        { name: \"ACID\",         hueFreq: 0.06,  huePhase: 0.0, sat: 1.0,  val: 1.0,  hueRange: 1.0,  flow: 0.8 },\n        { name: \"NEBULA\",       hueFreq: 0.006, huePhase: 0.7, sat: 0.9,  val: 0.25, hueRange: 0.4, flow: 0.1 },\n        { name: \"MOLTEN\",       hueFreq: 0.025, huePhase: 0.0, sat: 0.95, val: 0.9,  hueRange: 0.15, flow: -0.8 },\n        { name: \"DEEP SEA\",     hueFreq: 0.015, huePhase: 0.5, sat: 0.8,  val: 0.7,  hueRange: 0.35, flow: 0.15 },\n        { name: \"RADIOACTIVE\",  hueFreq: 0.02,  huePhase: 0.3, sat: 1.0,  val: 0.9,  hueRange: 0.12, flow: 0.0 },\n        { name: \"GOLDEN AGE\",   hueFreq: 0.008, huePhase: 0.05, sat: 0.4, val: 1.0,  hueRange: 0.1,  flow: 0.05 },\n        { name: \"GREYSCALE\",    hueFreq: 0.02,  huePhase: 0.0, sat: 0.0,  val: 0.9,  hueRange: 1.0,  flow: 0.0 },\n        { name: \"COTTON CANDY\", hueFreq: 0.02,  huePhase: 0.85, sat: 0.4, val: 1.0,  hueRange: 0.2,  flow: 0.0 }\n    ];\n\n    \/\/ 2. Default Home states\n    const M_HOME = { x: -(75n * SCALE_BITS) \/ 100n, y: 0n, z: 1.0 };\n    const J_HOME = { x: 0n, y: 0n, z: 0.4 };\n    const ZOOM_MAX = 3.0e35;\n\n    \/\/ 3. View State\n    let centerXBI = M_HOME.x, centerYBI = M_HOME.y, zoom = M_HOME.z;\n    let targetXBI = M_HOME.x, targetYBI = M_HOME.y, targetZoom = M_HOME.z;\n    let targetJulia = 0.0, currentJulia = 0.0, fadeAmount = 1.0;\n    let targetSeed = { x: 0n, y: 0n }, currentSeed = { x: 0n, y: 0n };\n    \n    \/\/ 4. Palette State (Now safe to reference palettes[0])\n    let palIdx = 0;\n    let curHF = palettes[0].hueFreq, curHP = palettes[0].huePhase;\n    let curSat = palettes[0].sat, curVal = palettes[0].val;\n    let curHR = palettes[0].hueRange, curFlow = palettes[0].flow;\n    let colorPhaseAccumulator = 0; \n    const paletteLerpSpeed = 0.04;\n\n\/\/ 5. Input & UI State\n    let keys = {}, mousePos = { x: 0, y: 0 }, lastMousePos = { x: 0, y: 0 };\n    let isDragging = false, hudVisible = true; \/\/ Removed duplicate tutorialActive from here\n    let bookmarks = JSON.parse(localStorage.getItem('fractalBookmarks') || '{}');\n\n    \/\/ --- TUTORIAL ---\n    let tutorialActive = true, tutorialStep = 0; \/\/ Keeping it here\n    const tutorialSteps = [\n        { \n            text: \"SCROLL or press 'Z'\/'X' to ZOOM.\\nPrecision tracking follows your cursor.\", \n            action: () => { \n                keys['z'] = true; \n                setTimeout(() => { \n                    keys['z'] = false; \n                    keys['x'] = true; \n                    setTimeout(() => { keys['x'] = false; }, 800); \n                }, 800); \n            }\n        },\n        { \n    text: \"DRAG to explore.\\nWarping to a distant coordinate...\", \n    action: () => {\n        fadeAmount = 0.0;\n        setTimeout(() => { \n            \/\/ Snapping current values slightly closer to destination for a 3s arrival\n            zoom = 100.0; \n            targetXBI = -1249796247275046747883026271997017172837167427350655114674176n;\n            targetYBI = 30849107504833643074612302621695567540318263354345562046464n;\n            targetZoom = 1307.58030;\n            fadeAmount = 1.0; \n        }, 200);\n    }\n},\n        { \n            text: \"Press 'R' to RESET the view back to initial coordinates and zoom depth.\", \n            action: () => {\n                keys['j'] = false; \n                if (targetJulia > 0.5) { \n                    targetXBI = J_HOME.x; targetYBI = J_HOME.y; targetZoom = J_HOME.z; palIdx = 0; \n                } else { \n                    targetXBI = M_HOME.x; targetYBI = M_HOME.y; targetZoom = M_HOME.z; palIdx = 0; \n                }\n            } \n        },\n        { \n            text: \"PRESS 'J' to MORPH into the Julia Set.\\nThe fractal shape changes based on time as you hold 'J'.\", \n            action: () => {\n                keys['j'] = true; \n                targetXBI = J_HOME.x; targetYBI = J_HOME.y; targetZoom = J_HOME.z;\n            } \n        },\n        { \n            text: \"Press 'M' to return to the Mandelbrot Set.\\nThis is the 'Map' of all possible Julia sets.\", \n            action: () => {\n                keys['j'] = false; \n                targetJulia = 0.0;\n                targetXBI = M_HOME.x; targetYBI = M_HOME.y; targetZoom = M_HOME.z;\n            } \n        },\n        { \n            text: \"Press 'H' to HIDE the interface.\\nImmerse yourself in the math.\", \n            action: () => {\n                hudVisible = false; hudR.style.opacity = hudL.style.opacity = \"0\";\n                setTimeout(() => { hudVisible = true; hudR.style.opacity = hudL.style.opacity = \"1\"; }, 1500);\n            }\n        },\n{ \n    text: \"Press SPACE to cycle THEMES.\\nFrom fiery lava to deep oceans...\", \n    action: () => {\n        \/\/ Find indices for specific palettes\n        const findPal = (name) => palettes.findIndex(p => p.name === name);\n        \n        \/\/ 1. Switch to MOLTEN\n        palIdx = findPal(\"MOLTEN\");\n        \n        \/\/ 2. After 2 seconds, switch to GREYSCALE\n        setTimeout(() => {\n            palIdx = findPal(\"GREYSCALE\");\n            \n            \/\/ 3. After 4 seconds total, switch to DEEP SEA\n            setTimeout(() => {\n                palIdx = findPal(\"DEEP SEA\");\n            }, 2000);\n        }, 2000);\n    } \n},\n        { \n            text: \"Tutorial Complete.\\nPress 'S' for a snapshot anytime.\\n\\n[Click to Finish]\", \n            action: () => { keys['j'] = false; } \n        }\n    ];\n\n    tutText.onclick = () => {\n        if (tutorialStep >= tutorialSteps.length) { \n            tutorialActive = false; \n            tutOverlay.style.display = \"none\"; \n            keys['j'] = false; \n            return; \n        }\n        tutText.innerText = tutorialSteps[tutorialStep].text; \n        tutorialSteps[tutorialStep].action(); \n        tutorialStep++;\n    }; \/\/ Added the missing closing bracket and semicolon here\n\n    \/\/ --- ENGINE CODE ---\n    const orbitBuffer = device.createBuffer({ size: 2000 * 16, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST });\n    const uniformBuffer = device.createBuffer({ size: 144, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });\n    const shaderModule = device.createShaderModule({ code: `\n        struct Uniforms { zoom: f32, aspect: f32, initZ: f32, iters: f32, res: vec2<f32>, julia: f32, hf: f32, hp: f32, sat: f32, val: f32, hr: f32, flow: f32, time: f32 };\n        @group(0) @binding(0) var<uniform> u: Uniforms;\n        @group(0) @binding(1) var<storage, read> orbit: array<vec4<f32>>;\n        fn hsv2rgb(c: vec3<f32>) -> vec3<f32> {\n            let K = vec4(1.0, 2.0 \/ 3.0, 1.0 \/ 3.0, 3.0);\n            let p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n            return c.z * mix(K.xxx, clamp(p - K.xxx, vec3(0.0), vec3(1.0)), c.y);\n        }\n        fn get_si(uv: vec2<f32>, limit: u32) -> f32 {\n            var dz = uv; let dc = mix(uv, vec2(0.0), u.julia); \n            for (var i: u32 = 0u; i < limit; i++) {\n                let ov = orbit[i]; let z_ref = vec2(ov.x + ov.y, ov.z + ov.w);\n                let next_dz = vec2(2.0*(z_ref.x*dz.x - z_ref.y*dz.y) + dz.x*dz.x - dz.y*dz.y + dc.x, 2.0*(z_ref.x*dz.y + z_ref.y*dz.x) + 2.0*dz.x*dz.y + dc.y);\n                dz = next_dz; let fz = z_ref + dz; if (dot(fz, fz) > 4096.0) { return f32(i) + 1.0 - log2(log2(length(fz))); }\n            }\n            return -1.0; \n        }\n        @vertex fn vs(@builtin(vertex_index) i: u32) -> @builtin(position) vec4<f32> {\n            var pos = array<vec2<f32>,3>(vec2(-1.,-1.), vec2(3.,-1.), vec2(-1.,3.));\n            return vec4<f32>(pos[i], 0.0, 1.0);\n        }\n        @fragment fn fs(@builtin(position) fpos: vec4<f32>) -> @location(0) vec4<f32> {\n            let scale = u.initZ \/ u.zoom;\n            let uv = (fpos.xy \/ u.res.xy * 2.0 - 1.0) * vec2(u.aspect, -1.0) * (scale * 0.5);\n            let si = get_si(uv, u32(u.iters));\n            if (si < 0.0) { return vec4(0.0, 0.0, 0.0, 1.0); }\n            let hue = u.hp + (fract((si * u.hf) + u.time) * u.hr);\n            var col = hsv2rgb(vec3(hue % 1.0, u.sat, u.val));\n            let eps = 0.0004 * scale;\n            let si_dx = get_si(uv + vec2(eps, 0.0), u32(u.iters)); let si_dy = get_si(uv + vec2(0.0, eps), u32(u.iters));\n            if (si_dx >= 0.0 && si_dy >= 0.0) {\n                let n = normalize(vec3(50.0 * (si_dx - si), 50.0 * (si_dy - si), 1.0));\n                let diffuse = max(dot(n, normalize(vec3(0.5, 0.5, 1.5))), 0.0);\n                col *= (diffuse + 0.15); col += (pow(diffuse, 40.0) * 0.4);\n            }\n            return vec4(col, 1.0);\n        }\n    `});\n\n    const pipeline = device.createRenderPipeline({ layout: 'auto', vertex: { module: shaderModule, entryPoint: \"vs\" }, fragment: { module: shaderModule, entryPoint: \"fs\", targets: [{ format: \"bgra8unorm\" }] } });\n\n    function calculateOrbit(cxi, cyi, sxi, syi, limit) {\n        const data = new Float32Array(2000 * 4);\n        let zx = 0n, zy = 0n; if (currentJulia > 0.5) { zx = cxi; zy = cyi; }\n        for (let i = 0; i < limit; i++) {\n            const zxf = Number(zx) \/ Number(SCALE_BITS), zyf = Number(zy) \/ Number(SCALE_BITS);\n            data[i*4] = Math.fround(zxf); data[i*4+1] = zxf - data[i*4];\n            data[i*4+2] = Math.fround(zyf); data[i*4+3] = zyf - data[i*4+2];\n            const x2 = (zx * zx) \/ SCALE_BITS, y2 = (zy * zy) \/ SCALE_BITS;\n            const sx = (currentJulia < 0.5) ? cxi : sxi, sy = (currentJulia < 0.5) ? cyi : syi;\n            const nzy = (2n * zx * zy) \/ SCALE_BITS + sy; zx = x2 - y2 + sx; zy = nzy;\n            if (x2 + y2 > (8000n * SCALE_BITS)) break;\n        }\n        return data;\n    }\n\nfunction render() {\n        \/\/ --- Continuous Inputs ---\n        if (keys['j']) {\n            targetJulia = 1.0;\n            const angle = Date.now() * 0.001; \n            targetSeed.x = BigInt(Math.round(Math.cos(angle) * 0.35 * Number(SCALE_BITS)));\n            targetSeed.y = BigInt(Math.round(Math.sin(angle) * 0.35 * Number(SCALE_BITS)));\n        }\n        if (keys['m']) targetJulia = 0.0;\n        \n        \/\/ Continuous Keyboard Zoom with Cursor Following\n        if (keys['z'] || keys['x']) {\n            const lastZ = targetZoom;\n            const zoomFact = keys['z'] ? 1.05 : 0.95;\n            targetZoom = Math.max(0.1, Math.min(ZOOM_MAX, targetZoom * zoomFact));\n            \n            \/\/ This logic makes the keyboard zoom center on the MOUSE cursor\n            const rect = canvas.getBoundingClientRect(), drift = (1 - (lastZ \/ targetZoom)), ws = (1.0 \/ lastZ) * 0.5;\n            targetXBI += BigInt(Math.round((((mousePos.x-rect.left)\/rect.width)*2-1)*(rect.width\/rect.height)*ws*drift*Number(SCALE_BITS)));\n            targetYBI += BigInt(Math.round((-(((mousePos.y-rect.top)\/rect.height)*2-1))*ws*drift*Number(SCALE_BITS)));\n        }\n\n        \/\/ --- Faster Interpolation (3-second target) ---\n        const tP = palettes[palIdx];\n        curHF += (tP.hueFreq - curHF) * paletteLerpSpeed;\n        curHP += (tP.huePhase - curHP) * paletteLerpSpeed;\n        curSat += (tP.sat - curSat) * paletteLerpSpeed;\n        curVal += (tP.val - curVal) * paletteLerpSpeed;\n        curHR += (tP.hueRange - curHR) * paletteLerpSpeed;\n        curFlow += (tP.flow - curFlow) * paletteLerpSpeed;\n        colorPhaseAccumulator += curFlow * 0.01;\n        \n        currentJulia += (targetJulia - currentJulia) * 0.1; \/\/ Faster (was 0.05)\n        currentSeed.x += BigInt(Math.round(Number(targetSeed.x - currentSeed.x) * 0.1));\n        currentSeed.y += BigInt(Math.round(Number(targetSeed.y - currentSeed.y) * 0.1));\n\n        \/\/ Faster, non-linear zoom interpolation\nconst zoomDiff = targetZoom \/ zoom;\nif (Math.abs(1 - zoomDiff) > 0.001) {\n    \/\/ If the difference is huge (like during a reset), move faster\n    const speed = (zoom > 1e10 || targetZoom > 1e10) ? 0.5 : 0.2;\n    zoom *= Math.pow(zoomDiff, speed); \n} else {\n    zoom = targetZoom;\n}\n\n\/\/ Snappier BigInt coordinates\n\/\/ Using a divisor of 2n instead of 4n or 10n for \"insanely fast\" tracking\ncenterXBI += (targetXBI - centerXBI) \/ 2n;\ncenterYBI += (targetYBI - centerYBI) \/ 2n;\n\n        \/\/ --- Render Logic ---\nconst baseIters = Math.floor(400 + Math.log2(zoom) * 40);        \n\/\/ If moving, we stay at the baseline. \n\/\/ If still, we \"sharpen\" the image by adding 400 more iterations.\nconst iters = (isDragging || keys['z'] || keys['x']) \n    ? baseIters \n    : baseIters + 400;\n\n\/\/ Cap it at 3000 so the GPU doesn't hang at extreme depths\nconst finalIters = Math.min(3000, iters);\n\n        device.queue.writeBuffer(orbitBuffer, 0, calculateOrbit(centerXBI, centerYBI, currentSeed.x, currentSeed.y, finalIters));\n        device.queue.writeBuffer(uniformBuffer, 0, new Float32Array([zoom, canvas.width\/canvas.height, 1.0, finalIters, canvas.width, canvas.height, currentJulia, curHF, curHP, curSat, curVal, curHR, curFlow, colorPhaseAccumulator]));\n        \n        const encoder = device.createCommandEncoder();\n        const pass = encoder.beginRenderPass({ colorAttachments: [{ view: context.getCurrentTexture().createView(), loadOp: \"clear\", storeOp: \"store\", clearValue: {r:0, g:0, b:0, a: 1.0 - fadeAmount} }] });\n        pass.setPipeline(pipeline);\n        pass.setBindGroup(0, device.createBindGroup({ layout: pipeline.getBindGroupLayout(0), entries: [{ binding: 0, resource: { buffer: uniformBuffer } }, { binding: 1, resource: { buffer: orbitBuffer } }] }));\n        pass.draw(3); pass.end(); device.queue.submit([encoder.finish()]);\n\nif (hudVisible) {\n            \/\/ Right HUD: Theme and Raw Zoom\n            hudR.innerHTML = `\n                <div style=\"font-size: 12px; font-weight: bold; margin-bottom: 4px;\">THEME: ${palettes[palIdx].name}<\/div>\n                ZOOM: ${Math.floor(zoom).toString()}\n            `;\n\n            \/\/ Left HUD: Raw 60-digit BigInts\n            \/\/ We use .toString() to show every single digit of the high-precision coordinate\n            hudL.innerHTML = `\n                RE: ${centerXBI.toString()}<br>\n                IM: ${centerYBI.toString()}\n            `;\n        }\n        requestAnimationFrame(render);\n    }\n\n    \/\/ --- INPUTS ---\nhudL.onclick = () => {\n        const codeSnippet = `targetXBI = ${targetXBI.toString()}n;\\ntargetYBI = ${targetYBI.toString()}n;\\ntargetZoom = ${targetZoom.toFixed(5)};`;\n\n        navigator.clipboard.writeText(codeSnippet).then(() => {\n            notify.innerText = \"RAW BIGINT COPIED\";\n            notify.style.opacity = 1;\n            setTimeout(() => notify.style.opacity = 0, 1500);\n        });\n    };\n\n    window.onmousedown = e => { if (tutorialActive && e.target === tutText) return; isDragging = true; lastMousePos = { x: e.clientX, y: e.clientY }; };\n    window.onmouseup = () => isDragging = false;\n    window.onmousemove = e => { \n        mousePos = { x: e.clientX, y: e.clientY }; \/\/ Always update position\n        if (isDragging) {\n            const dx = e.clientX - lastMousePos.x, dy = e.clientY - lastMousePos.y, rect = canvas.getBoundingClientRect(), ws = (1.0 \/ targetZoom) * 0.5;\n            targetXBI -= BigInt(Math.round((dx \/ rect.width) * 2 * (rect.width\/rect.height) * ws * Number(SCALE_BITS)));\n            targetYBI += BigInt(Math.round((dy \/ rect.height) * 2 * ws * Number(SCALE_BITS)));\n            lastMousePos = { x: e.clientX, y: e.clientY };\n        }\n    };    \n    window.onkeydown = e => {\n        const k = e.key.toLowerCase(); keys[k] = true;\n        if (k === 'j') { targetXBI = J_HOME.x; targetYBI = J_HOME.y; targetZoom = J_HOME.z; }\n        if (k === 'm') { targetXBI = M_HOME.x; targetYBI = M_HOME.y; targetZoom = M_HOME.z; }\n        if (k === 'r') { \n    \/\/ Snap current values to target immediately if we are super deep\n    if (zoom > 1e10) {\n        \/\/ We \"teleport\" partway home so the glide is snappy\n        centerXBI = targetJulia > 0.5 ? J_HOME.x : M_HOME.x;\n        centerYBI = targetJulia > 0.5 ? J_HOME.y : M_HOME.y;\n        zoom = 100.0; \/\/ Drop from 1e35 to 100 instantly\n    }\n    \n    \/\/ Set the final targets\n    if (targetJulia > 0.5) { \n        targetXBI = J_HOME.x; targetYBI = J_HOME.y; targetZoom = J_HOME.z; \n    } else { \n        targetXBI = M_HOME.x; targetYBI = M_HOME.y; targetZoom = M_HOME.z; \n    }\n}\n        if (k === ' ') palIdx = (palIdx + 1) % palettes.length;\n        if (k === 'h') { hudVisible = !hudVisible; hudR.style.opacity = hudL.style.opacity = hudVisible ? \"1\" : \"0\"; }\n        if (k === 'escape' && tutorialActive) { tutorialActive = false; tutOverlay.style.display = \"none\"; fadeAmount = 1.0; keys['j'] = false;}\n        if (k === 's') { const link = document.createElement('a'); link.download = `fractal-${Date.now()}.png`; link.href = canvas.toDataURL('image\/png'); link.click(); }\n    };\n\n    window.onkeyup = e => { keys[e.key.toLowerCase()] = false; };\n    \n    window.onwheel = e => { \n        e.preventDefault(); \n        const lastZ = targetZoom; \n        targetZoom = Math.max(0.1, Math.min(ZOOM_MAX, targetZoom \/ Math.exp(e.deltaY * 0.001)));\n        const rect = canvas.getBoundingClientRect(), drift = (1 - (lastZ \/ targetZoom)), ws = (1.0 \/ lastZ) * 0.5;\n        targetXBI += BigInt(Math.round((((e.clientX-rect.left)\/rect.width)*2-1)*(rect.width\/rect.height)*ws*drift*Number(SCALE_BITS)));\n        targetYBI += BigInt(Math.round((-(((e.clientY-rect.top)\/rect.height)*2-1))*ws*drift*Number(SCALE_BITS)));\n    }, { passive: false };\n\n    const resz = () => { canvas.width = window.innerWidth * dpr; canvas.height = window.innerHeight * dpr; context.configure({ device, format: \"bgra8unorm\", alphaMode: \"opaque\" }); };\n    window.onresize = resz; resz(); render();\n})();\n<\/script>\n","protected":false},"excerpt":{"rendered":"<p>SAVED THEME: ACIDZOOM: 1.00e+0 RE: -7.50e-1IM: 0.00e+0 FRACTAL EXPLORER*PHOTOSENSITIVITY WARNING* [CLICK to Begin Walkthrough] [ESC to Exit]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"nf_dc_page":"","om_disable_all_campaigns":false,"inline_featured_image":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"class_list":["post-4941","page","type-page","status-publish","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.4 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Fractal Explorer - Mina Systems<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/mina.systems\/ja\/fractal_explorer\/\" \/>\n<meta property=\"og:locale\" content=\"ja_JP\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Fractal Explorer - Mina Systems\" \/>\n<meta property=\"og:description\" content=\"SAVED THEME: ACIDZOOM: 1.00e+0 RE: -7.50e-1IM: 0.00e+0 FRACTAL EXPLORER*PHOTOSENSITIVITY WARNING* [CLICK to Begin Walkthrough] [ESC to Exit]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/mina.systems\/ja\/fractal_explorer\/\" \/>\n<meta property=\"og:site_name\" content=\"Mina Systems\" \/>\n<meta property=\"article:modified_time\" content=\"2026-01-10T14:38:04+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"\u63a8\u5b9a\u8aad\u307f\u53d6\u308a\u6642\u9593\" \/>\n\t<meta name=\"twitter:data1\" content=\"1\u5206\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/mina.systems\\\/fractal_explorer\\\/\",\"url\":\"https:\\\/\\\/mina.systems\\\/fractal_explorer\\\/\",\"name\":\"Fractal Explorer - Mina Systems\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/mina.systems\\\/#website\"},\"datePublished\":\"2026-01-10T00:09:47+00:00\",\"dateModified\":\"2026-01-10T14:38:04+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/mina.systems\\\/fractal_explorer\\\/#breadcrumb\"},\"inLanguage\":\"ja\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/mina.systems\\\/fractal_explorer\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/mina.systems\\\/fractal_explorer\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/mina.systems\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Fractal Explorer\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/mina.systems\\\/#website\",\"url\":\"https:\\\/\\\/mina.systems\\\/\",\"name\":\"Mina Systems\",\"description\":\"Artist\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/mina.systems\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"ja\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Fractal Explorer - Mina Systems","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/mina.systems\/ja\/fractal_explorer\/","og_locale":"ja_JP","og_type":"article","og_title":"Fractal Explorer - Mina Systems","og_description":"SAVED THEME: ACIDZOOM: 1.00e+0 RE: -7.50e-1IM: 0.00e+0 FRACTAL EXPLORER*PHOTOSENSITIVITY WARNING* [CLICK to Begin Walkthrough] [ESC to Exit]","og_url":"https:\/\/mina.systems\/ja\/fractal_explorer\/","og_site_name":"Mina Systems","article_modified_time":"2026-01-10T14:38:04+00:00","twitter_card":"summary_large_image","twitter_misc":{"\u63a8\u5b9a\u8aad\u307f\u53d6\u308a\u6642\u9593":"1\u5206"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/mina.systems\/fractal_explorer\/","url":"https:\/\/mina.systems\/fractal_explorer\/","name":"Fractal Explorer - Mina Systems","isPartOf":{"@id":"https:\/\/mina.systems\/#website"},"datePublished":"2026-01-10T00:09:47+00:00","dateModified":"2026-01-10T14:38:04+00:00","breadcrumb":{"@id":"https:\/\/mina.systems\/fractal_explorer\/#breadcrumb"},"inLanguage":"ja","potentialAction":[{"@type":"ReadAction","target":["https:\/\/mina.systems\/fractal_explorer\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/mina.systems\/fractal_explorer\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/mina.systems\/"},{"@type":"ListItem","position":2,"name":"Fractal Explorer"}]},{"@type":"WebSite","@id":"https:\/\/mina.systems\/#website","url":"https:\/\/mina.systems\/","name":"Mina Systems","description":"Artist","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/mina.systems\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"ja"}]}},"jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/mina.systems\/ja\/wp-json\/wp\/v2\/pages\/4941","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mina.systems\/ja\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/mina.systems\/ja\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/mina.systems\/ja\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mina.systems\/ja\/wp-json\/wp\/v2\/comments?post=4941"}],"version-history":[{"count":20,"href":"https:\/\/mina.systems\/ja\/wp-json\/wp\/v2\/pages\/4941\/revisions"}],"predecessor-version":[{"id":5053,"href":"https:\/\/mina.systems\/ja\/wp-json\/wp\/v2\/pages\/4941\/revisions\/5053"}],"wp:attachment":[{"href":"https:\/\/mina.systems\/ja\/wp-json\/wp\/v2\/media?parent=4941"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}