2026-08-22 20:55:27 +09:00
<!doctype html>
< html lang = "ja" >
< head >
< meta charset = "utf-8" >
< meta name = "viewport" content = "width=device-width,initial-scale=1,viewport-fit=cover" >
< meta name = "theme-color" content = "#050813" >
2026-08-23 17:12:29 +09:00
< title > Mandelbrot Deep Zoom v24.1.3 WebGPU< / title >
2026-08-22 20:55:27 +09:00
< style >
:root{color-scheme:dark;--panel:rgba(7,12,25,.88);--line:rgba(255,255,255,.12);--text:#f7f8ff;--muted:#a9b3ca;--accent:#61dbe9}
*{box-sizing:border-box}html,body{margin:0;width:100%;height:100%;overflow:hidden;background:#050813;font-family:Inter,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}body{-webkit-user-select:none;user-select:none}
#view{position:fixed;inset:0;width:100%;height:100%;display:block;background:#050813;image-rendering:auto;touch-action:none}
.top{position:fixed;z-index:5;top:max(10px,env(safe-area-inset-top));left:10px;right:10px;display:flex;gap:8px;pointer-events:none}.brand,.stats,.panel,.toast{backdrop-filter:blur(18px) saturate(130%);-webkit-backdrop-filter:blur(18px) saturate(130%)}
.brand{pointer-events:auto;background:var(--panel);border:1px solid var(--line);border-radius:16px;padding:10px 14px;font-weight:850;letter-spacing:.04em;font-size:13px;box-shadow:0 12px 40px rgba(0,0,0,.32)}.brand small{display:block;margin-top:2px;color:var(--muted);font-size:10px;font-weight:600;letter-spacing:0}
.stats{margin-left:auto;max-width:min(560px,65vw);padding:9px 12px;border:1px solid var(--line);border-radius:14px;background:var(--panel);font:11px/1.45 ui-monospace,SFMono-Regular,Menlo,monospace;overflow:hidden}.row{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.muted{color:var(--muted)}
.panel{position:fixed;z-index:6;right:10px;bottom:max(10px,env(safe-area-inset-bottom));width:min(380px,calc(100vw - 20px));padding:11px;border:1px solid var(--line);border-radius:19px;background:var(--panel);box-shadow:0 18px 58px rgba(0,0,0,.44)}
.toolbar{display:grid;grid-template-columns:repeat(4,1fr);gap:7px}select{appearance:auto;border:1px solid rgba(255,255,255,.14);background:rgba(255,255,255,.07);color:var(--text);min-height:44px;padding:4px 8px;border-radius:10px;font-size:12px}#palette{color:#111;background:#f4f5f8}#palette option{color:#111;background:#fff}button{appearance:none;border:1px solid rgba(255,255,255,.14);background:rgba(255,255,255,.07);color:var(--text);min-height:44px;padding:7px 5px;border-radius:12px;font-size:12px;font-weight:760;cursor:pointer}button:active{transform:translateY(1px)}button.primary{background:linear-gradient(135deg,rgba(64,215,236,.25),rgba(139,78,255,.22));border-color:rgba(97,219,233,.48)}button.on{outline:1px solid rgba(97,219,233,.8)}button:focus-visible,select:focus-visible,input:focus-visible,#view:focus-visible{outline:3px solid #fff;outline-offset:2px}
.group{margin-top:10px;padding-top:9px;border-top:1px solid rgba(255,255,255,.08)}.line{display:grid;grid-template-columns:98px 1fr 48px;align-items:center;gap:8px;margin:7px 0}.line label{font-size:12px;color:#dce1ef}.line output{text-align:right;color:var(--muted);font:11px ui-monospace,monospace}input[type=range]{width:100%;min-height:44px;accent-color:var(--accent)}.checks{display:flex;gap:12px;flex-wrap:wrap;margin-top:8px;color:#dce1ef;font-size:12px}.checks label{display:flex;align-items:center;min-height:44px;gap:6px}
details{margin-top:9px;border-top:1px solid rgba(255,255,255,.08);padding-top:8px}summary{display:flex;align-items:center;min-height:44px;cursor:pointer;color:var(--muted);font-size:12px}.exact-grid{display:grid;grid-template-columns:54px 1fr;gap:6px;margin-top:8px}.exact-grid input{min-width:0;width:100%;min-height:44px;border:1px solid var(--line);border-radius:8px;background:#070c19;color:var(--text);padding:6px;font:11px ui-monospace,monospace}.mini-actions{display:flex;gap:6px;margin-top:7px}.mini-actions button{flex:1}
.diagnostics{margin-top:8px;color:var(--muted);font:10.5px/1.5 ui-monospace,monospace;white-space:pre-wrap;overflow-wrap:anywhere}.exact-grid input{user-select:text;-webkit-user-select:text}
.bottom{display:flex;align-items:center;justify-content:space-between;gap:8px}.badge{display:inline-flex;align-items:center;gap:6px;padding:4px 8px;border-radius:999px;background:rgba(255,255,255,.07);font-size:10px;color:#d9dfed}.dot{width:7px;height:7px;border-radius:50%;background:#61dbe9;box-shadow:0 0 12px #61dbe9}.hint{margin-top:8px;color:var(--muted);font-size:10.5px;line-height:1.45}
.toast{position:fixed;z-index:10;left:50%;bottom:24px;transform:translate(-50%,16px);opacity:0;transition:.18s;pointer-events:none;padding:9px 12px;border:1px solid var(--line);border-radius:12px;background:rgba(7,12,25,.95);font-size:12px}.toast.show{opacity:1;transform:translate(-50%,0)}
dialog{width:min(430px,calc(100vw - 24px));border:1px solid var(--line);border-radius:18px;background:#0b1120;color:var(--text);padding:16px;box-shadow:0 24px 80px #000}dialog::backdrop{background:rgba(0,0,0,.65)}dialog h2{font-size:16px;margin:0 0 12px}.export-grid{display:grid;grid-template-columns:130px 1fr;gap:10px;align-items:center}.export-grid label{font-size:12px}.export-grid input,.export-grid select{width:100%}.export-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:14px}progress{width:100%;margin-top:12px}
#uiToggle{position:fixed;z-index:20;left:max(10px,env(safe-area-inset-left));bottom:max(10px,env(safe-area-inset-bottom));min-width:52px;min-height:44px;padding:8px 12px;border-radius:999px;background:rgba(7,12,25,.78);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);box-shadow:0 8px 30px rgba(0,0,0,.3)}body.ui-hidden .top,body.ui-hidden .panel{display:none}body.ui-hidden #uiToggle{background:rgba(7,12,25,.7)}
.compact-status{display:none;position:fixed;z-index:4;right:8px;top:max(8px,env(safe-area-inset-top));max-width:58vw;padding:7px 10px;border:1px solid var(--line);border-radius:999px;background:var(--panel);font-size:11px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
@media(max-width:700px){.stats{display:none}.brand small{display:none}.panel{left:8px;right:8px;bottom:max(8px,env(safe-area-inset-bottom));width:auto;padding:10px;touch-action:pan-x pan-y pinch-zoom}.line{grid-template-columns:82px 1fr 42px}.hint{display:none}.compact-status{display:block}button,select{min-height:44px}}
@media(prefers-reduced-motion:reduce){.toast{transition:none}button:active{transform:none}}
@media(prefers-reduced-transparency:reduce){.brand,.stats,.panel,.toast,#uiToggle,.compact-status{backdrop-filter:none;-webkit-backdrop-filter:none;background:#0b1120}}
< / style >
< / head >
< body >
< canvas id = "view" tabindex = "0" role = "img" aria-label = "マンデルブロ集合。矢印キーで移動、Enterで拡大、Shift+Enterで縮小できます" > < / canvas >
< div class = "top" > < div class = "brand" > MANDELBROT DEEP ZOOM< / div > < div class = "stats" role = "status" aria-live = "polite" aria-atomic = "true" > < div class = "row" > < span class = "muted" > 中心< / span > < span id = "coord" > …< / span > < / div > < div class = "row" > < span class = "muted" > 倍率< / span > < span id = "zoom" > …< / span > < span class = "muted" > 表示幅< / span > < span id = "span" > …< / span > < / div > < div class = "row" > < span class = "muted" > 計算< / span > < span id = "engine" > 起動中…< / span > < span class = "muted" > 描画< / span > < span id = "render" > …< / span > < / div > < / div > < / div >
< div id = "compactStatus" class = "compact-status" role = "status" aria-live = "polite" > 起動中< / div >
< div id = "controls" class = "panel" >
< div class = "toolbar" > < button id = "zin" aria-label = "中心を拡大" > + < / button > < button id = "zout" aria-label = "中心を縮小" > − < / button > < button id = "reset" > リセット< / button > < button id = "png" > 出力< / button > < / div >
< div class = "group" >
2026-08-23 17:12:29 +09:00
< div class = "line" > < label for = "processMode" > 処理モード< / label > < select id = "processMode" > < option value = "power" > 省電力< / option > < option value = "standard" selected > 標準< / option > < option value = "fine" > 精細< / option > < option value = "validate" > 保守的 (Strict)< / option > < / select > < output > < / output > < / div >
2026-08-22 20:55:27 +09:00
< div class = "line" > < label for = "palette" > 彩色< / label > < select id = "palette" > < option value = "0" > 昼夜< / option > < option value = "1" > 虹色< / option > < option value = "2" > 白黒< / option > < / select > < output > < / output > < / div >
< div class = "line" > < label for = "cycle" > 色周期< / label > < input id = "cycle" type = "range" min = "0.001" max = "0.05" step = "0.0005" value = "0.008" > < output id = "cycleO" for = "cycle" > 0.0080< / output > < / div >
< div class = "line" > < label for = "shift" > 色相位置< / label > < input id = "shift" type = "range" min = "0" max = "1" step = "0.005" value = "0.18" > < output id = "shiftO" for = "shift" > .18< / output > < / div >
< details > < summary > 詳細設定・正確な座標< / summary >
< div class = "line" > < label for = "iters" > 基準反復< / label > < input id = "iters" type = "range" min = "100" max = "2500" step = "25" value = "350" > < output id = "itersO" for = "iters" > 350< / output > < / div >
2026-08-23 17:12:29 +09:00
< div class = "checks" > < label > < input id = "adaptive" type = "checkbox" checked > 反復回数を自動調整< / label > < label > < input id = "hq" type = "checkbox" > GPU境界平滑化< / label > < / div >
2026-08-22 20:55:27 +09:00
< div class = "exact-grid" > < label for = "coordReInput" > 実部< / label > < input id = "coordReInput" > < label for = "coordImInput" > 虚部< / label > < input id = "coordImInput" > < label for = "coordSpanInput" > 表示幅< / label > < input id = "coordSpanInput" > < / div >
< div class = "mini-actions" > < button id = "coordApply" > 座標を適用< / button > < button id = "coordCopy" > 正確値をコピー< / button > < button id = "undoView" > 戻す< / button > < button id = "redoView" > 進む< / button > < / div >
< / details >
< details > < summary > 診断情報< / summary > < div class = "diagnostics" > < div id = "diagEngine" > engine: …< / div > < div id = "diagNumeric" > numeric: …< / div > < div id = "diagMemory" > memory: …< / div > < / div > < / details >
< / div >
< div class = "group bottom" > < span class = "badge" > < span class = "dot" > < / span > < span id = "badge" > 起動中< / span > < / span > < div style = "display:flex;gap:6px" > < button id = "share" > URL共有< / button > < / div > < / div >
< div class = "hint" > ホイール / ピンチでズーム、ドラッグで移動。HキーでUI表示を切り替え。< / div >
< / div >
< button id = "uiToggle" title = "UIを隠す / 表示" aria-controls = "controls" aria-expanded = "true" > UI− < / button >
< div id = "toast" class = "toast" role = "status" aria-live = "polite" > < / div >
< dialog id = "exportDialog" aria-labelledby = "exportTitle" >
< h2 id = "exportTitle" > 高解像度 PNG 出力< / h2 >
< div class = "export-grid" >
< label for = "exportScale" > 出力倍率< / label > < select id = "exportScale" > < option value = "1" > 1× < / option > < option value = "2" > 2× < / option > < option value = "4" > 4× < / option > < option value = "0" > カスタム幅< / option > < / select >
< label for = "exportWidth" > 幅( px) < / label > < input id = "exportWidth" type = "number" min = "64" max = "16384" step = "1" >
< label for = "exportAA" > サブサンプル< / label > < select id = "exportAA" > < option value = "1" > 1× ( 高速) < / option > < option value = "2" > 2× 2 AA< / option > < / select >
2026-08-23 17:12:29 +09:00
< label for = "exportPrecision" > 精度方針< / label > < select id = "exportPrecision" > < option value = "balanced" > Balanced< / option > < option value = "strict" > 保守的 (Strict)< / option > < / select >
2026-08-22 20:55:27 +09:00
< / div >
< progress id = "exportProgress" max = "1" value = "0" hidden > < / progress >
< div id = "exportStatus" role = "status" aria-live = "polite" > < / div >
< div class = "export-actions" > < button id = "exportCancel" type = "button" > 閉じる< / button > < button id = "exportQuick" type = "button" > 表示を即時保存< / button > < button id = "exportStart" class = "primary" type = "button" > PNGを生成< / button > < / div >
< / dialog >
2026-08-23 17:12:29 +09:00
< script src = "gpu-kernels.js" > < / script >
2026-08-22 20:55:27 +09:00
< script src = "script.js" > < / script >
< / body >
< / html >