Fractal Explorer
North Mini Code 1.0 GGUF · typescript

Browser check: Artifact runtime error. This is a technical check, not a quality review.
Reviews & generation details
Reviews
No human review yet. Unreviewed does not mean unsuccessful.
Generation context
Imported historical artifact. Its exact original instructions and lineage are not known.
Generation prompt · exact text unavailable
See the current prompt for reference. It may differ from the instructions used to generate this artifact.
Inspect original source 35,190 bytes · SHA-256 1c73c4957596
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Fractal Explorer</title>
<style>
/* ── css: layout ── */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
}
body {
margin: 0;
padding: 0;
background: #000;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
width: 100vw;
}
canvas {
display: block;
width: 100%;
height: 100%;
image-rendering: crisp-optimizeSpeed;
image-rendering: -moz-crisp-optimize;
image-rendering: pixelated;
}
.hud {
position: absolute;
top: 10px;
left: 10px;
background: rgba(0, 0, 0, 0.7);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 8px;
padding: 10px 15px;
font-size: 14px;
color: #fff;
transition: opacity 0.3s ease, transform 0.3s ease;
pointer-events: auto;
z-index: 100;
backdrop-filter: blur(5px);
-webkit-backdrop-filter: blur(5px);
}
.hud.hidden {
opacity: 0;
transform: translateY(-20px);
pointer-events: none;
}
.hud-content > span {
margin-right: 15px;
display: inline-block;
}
.hud-toggle {
position: absolute;
top: 5px;
right: 5px;
cursor: pointer;
font-size: 16px;
opacity: 0.7;
transition: opacity 0.2s;
}
.hud-toggle:hover {
opacity: 1;
}
.palette-selector {
position: absolute;
bottom: 10px;
left: 10px;
display: flex;
gap: 8px;
background: rgba(0, 0, 0, 0.7);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 8px;
padding: 8px;
z-index: 200;
backdrop-filter: blur(5px);
-webkit-backdrop-filter: blur(5px);
}
.palette-btn {
width: 40px;
height: 30px;
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 4px;
cursor: pointer;
transition: transform 0.1s;
}
.palette-btn:hover {
transform: scale(1.1);
border-color: #fff;
}
.palette-btn.active {
border: 2px solid #fff;
transform: scale(1.05);
}
.zoom-indicator {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 50;
}
.zoom-box {
position: absolute;
border: 2px solid rgba(255, 255, 255, 0.8);
background: rgba(255, 255, 255, 0.1);
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.5);
pointer-events: none;
transition: all 0.1s ease;
}
.zoom-indicator.active .zoom-box {
background: rgba(255, 255, 255, 0.05);
}
</style>
</head>
<body>
<div id="app"></div>
<canvas id="fractalCanvas" width="800" height="600"></canvas>
<div id="hud" class="hud">
<div class="hud-content">
<span class="mag">×1.0</span>
<span class="coords">(0.0, 0.0)</span>
<span class="iter">Iter: 256</span>
<span class="fps">FPS: 0</span>
</div>
<div class="hud-toggle" title="Toggle HUD">⚙️</div>
</div>
<div id="paletteSelector" class="palette-selector">
<button class="palette-btn" data-palette="fire" style="background: linear-gradient(to right, #000, #f00, #f80, #fff)"></button>
<button class="palette-btn" data-palette="ocean" style="background: linear-gradient(to right, #000, #0326, #96cf, #fff)"></button>
<button class="palette-btn" data-palette="monochrome" style="background: linear-gradient(to right, #000, #808080, #fff)"></button>
<button class="palette-btn" data-palette="psychedelic" style="background: linear-gradient(to right, #f0f, #0ff, #0f0, #ff0, #f00)"></button>
</div>
<div id="zoomIndicator" class="zoom-indicator" style="display:none;">
<div class="zoom-box"></div>
</div>
<script>
(function () {
'use strict';
var __mods = {};
var __cache = {};
var __map = {"src/main.ts":{"./constants":"src/constants.ts","./render":"src/render.ts"},"src/render.ts":{"./constants":"src/constants.ts"}};
function __require(id) {
if (__cache[id]) return __cache[id].exports;
var module = __cache[id] = { exports: {} };
var factory = __mods[id];
if (!factory) throw new Error("Module not found: " + id);
factory(module.exports, function (spec) {
var target = (__map[id] && __map[id][spec]) || spec;
return __require(target);
}, module);
return module.exports;
}
// ── module: src/main.ts ──
__mods["src/main.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FractalExplorer = void 0;
// Application state and main controller
const constants_1 = require("./constants");
const render_1 = require("./render");
class FractalExplorer {
constructor(canvas) {
this.lastFrameTime = 0;
this.fps = 0;
this.animationId = null;
this.isDragging = false;
this.dragStart = null;
this.zoomBoxVisible = false;
this.zoomBox = null;
this.lastPointer = null;
this.panStart = null;
this.history = [];
this.historyIndex = -1;
this.hudVisible = true;
this.hudTimeout = null;
this.resizeObserver = null;
this.canvas = canvas;
this.ctx = canvas.getContext('2d');
this.renderer = new render_1.ProgressiveRenderer();
this.palettes = constants_1.CONSTANTS.PALETTES;
this.state = this.loadState();
this.render();
this.setupEventListeners();
this.setupUI();
this.startAnimationLoop();
this.startFPSMonitor();
}
loadState() {
const saved = localStorage.getItem('fractalExplorerState');
if (saved) {
try {
const parsed = JSON.parse(saved);
// Convert color arrays back to proper format
parsed.palette = parsed.palette.map((p) => ({
offset: p.offset,
color: p.color
}));
return parsed;
}
catch (e) {
console.warn('Failed to parse saved state:', e);
}
}
// Default state
return {
reMin: -2.0,
imMax: 2.0,
scale: 3.0 / Math.min(this.canvas.width, this.canvas.height),
palette: this.palettes[constants_1.CONSTANTS.DEFAULT_PALETTE],
colorOffset: 0,
isJulia: false,
cReal: -0.8,
cImag: 0.156,
maxIter: constants_1.CONSTANTS.MAX_ITER_BASE,
};
}
saveState() {
const stateToSave = { ...this.state };
// Ensure palette is serializable
stateToSave.palette = stateToSave.palette.map(p => ({
offset: p.offset,
color: p.color
}));
localStorage.setItem('fractalExplorerState', JSON.stringify(stateToSave));
}
setupUI() {
// Palette buttons
document.querySelectorAll('.palette-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
const paletteKey = e.currentTarget.dataset.palette;
if (paletteKey && this.palettes[paletteKey]) {
this.state.palette = this.palettes[paletteKey];
this.render();
this.saveState();
this.updateActivePaletteButton(paletteKey);
}
});
});
// HUD toggle
const hudToggle = document.querySelector('.hud-toggle');
hudToggle?.addEventListener('click', () => {
this.hudVisible = !this.hudVisible;
const hud = document.querySelector('.hud');
if (hud) {
hud.classList.toggle('hidden', !this.hudVisible);
}
this.resetHUDTimeout();
});
// Keyboard shortcuts
document.addEventListener('keydown', (e) => {
switch (e.key.toLowerCase()) {
case 'j':
this.toggleJuliaMode();
break;
case '=':
case '+':
this.zoom(1.15, this.canvas.width / 2, this.canvas.height / 2);
break;
case '-':
this.zoom(1 / 1.15, this.canvas.width / 2, this.canvas.height / 2);
break;
case 'r':
this.resetView();
break;
}
});
// Handle window resize
this.resizeObserver = new ResizeObserver(() => {
this.updateCanvasSize();
});
this.resizeObserver.observe(this.canvas);
}
setupEventListeners() {
// Mouse wheel zoom
this.canvas.addEventListener('wheel', (e) => {
e.preventDefault();
const rect = this.canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
const zoomFactor = e.deltaY > 0 ? 1 / constants_1.CONSTANTS.ZOOM_SPEED : constants_1.CONSTANTS.ZOOM_SPEED;
this.zoom(zoomFactor, x, y);
this.resetHUDTimeout();
});
// Mouse down for drag zoom or pan
this.canvas.addEventListener('mousedown', (e) => {
if (e.button === 0) { // Left click only
e.preventDefault();
const rect = this.canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
if (e.shiftKey) {
// Start panning
this.panStart = {
x: e.clientX,
y: e.clientY,
reMin: this.state.reMin,
imMax: this.state.imMax,
};
}
else {
// Start drag zoom selection
this.isDragging = true;
this.dragStart = { x, y };
this.zoomBoxVisible = true;
this.zoomBox = { x, y, width: 0, height: 0 };
const zoomIndicator = document.querySelector('.zoom-indicator');
zoomIndicator.classList.add('active');
}
this.resetHUDTimeout();
}
});
// Mouse move for drag zoom or pan
this.canvas.addEventListener('mousemove', (e) => {
if (this.panStart) {
const deltaX = (e.clientX - this.panStart.x) / this.canvas.width * (this.state.reMin - this.state.reMin / constants_1.CONSTANTS.ZOOM_SPEED);
const deltaY = (e.clientY - this.panStart.y) / this.canvas.height * (this.state.imMax / constants_1.CONSTANTS.ZOOM_SPEED);
this.state.reMin = this.panStart.reMin - deltaX;
this.state.imMax = this.panStart.imMax + deltaY;
this.render();
}
if (this.isDragging && this.dragStart) {
const rect = this.canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
this.zoomBox.x = Math.min(this.dragStart.x, x);
this.zoomBox.y = Math.min(this.dragStart.y, y);
this.zoomBox.width = Math.abs(x - this.dragStart.x);
this.zoomBox.height = Math.abs(y - this.dragStart.y);
const zoomIndicator = document.querySelector('.zoom-indicator');
zoomIndicator.style.display = 'block';
}
});
// Mouse up to end drag zoom or pan
this.canvas.addEventListener('mouseup', () => {
if (this.panStart) {
this.panStart = null;
}
if (this.isDragging && this.zoomBox && this.zoomBox.width > 10 && this.zoomBox.height > 10) {
// Perform zoom to selected region
const scale = this.state.scale / (this.zoomBox.width / this.canvas.width);
const reMin = this.state.reMin + this.zoomBox.x / this.canvas.width * (this.state.reMin - this.state.reMin / scale);
const imMax = this.state.imMax - this.zoomBox.y / this.canvas.height * (this.state.imMax / scale);
this.zoomToRegion(reMin, imMax, scale);
}
this.isDragging = false;
this.dragStart = null;
this.zoomBoxVisible = false;
const zoomIndicator = document.querySelector('.zoom-indicator');
zoomIndicator.classList.remove('active');
zoomIndicator.style.display = 'none';
this.zoomBox = null;
});
// Touch support for mobile
this.canvas.addEventListener('touchstart', (e) => {
e.preventDefault();
if (e.touches.length === 1) {
const touch = e.touches[0];
const rect = this.canvas.getBoundingClientRect();
const x = touch.clientX - rect.left;
const y = touch.clientY - rect.top;
this.isDragging = true;
this.dragStart = { x, y };
this.zoomBoxVisible = true;
this.zoomBox = { x, y, width: 0, height: 0 };
const zoomIndicator = document.querySelector('.zoom-indicator');
zoomIndicator.classList.add('active');
zoomIndicator.style.display = 'block';
}
});
this.canvas.addEventListener('touchmove', (e) => {
e.preventDefault();
if (this.isDragging && this.dragStart && this.zoomBox) {
const touch = e.touches[0];
const rect = this.canvas.getBoundingClientRect();
const x = touch.clientX - rect.left;
const y = touch.clientY - rect.top;
this.zoomBox.x = Math.min(this.dragStart.x, x);
this.zoomBox.y = Math.min(this.dragStart.y, y);
this.zoomBox.width = Math.abs(x - this.dragStart.x);
this.zoomBox.height = Math.abs(y - this.dragStart.y);
}
});
this.canvas.addEventListener('touchend', (e) => {
e.preventDefault();
if (this.isDragging && this.zoomBox && this.zoomBox.width > 10 && this.zoomBox.height > 10) {
const scale = this.state.scale / (this.zoomBox.width / this.canvas.width);
const reMin = this.state.reMin + this.zoomBox.x / this.canvas.width * (this.state.reMin - this.state.reMin / scale);
const imMax = this.state.imMax - this.zoomBox.y / this.canvas.height * (this.state.imMax / scale);
this.zoomToRegion(reMin, imMax, scale);
}
this.isDragging = false;
this.dragStart = null;
this.zoomBoxVisible = false;
const zoomIndicator = document.querySelector('.zoom-indicator');
zoomIndicator.classList.remove('active');
zoomIndicator.style.display = 'none';
this.zoomBox = null;
});
}
resetHUDTimeout() {
if (this.hudTimeout) {
clearTimeout(this.hudTimeout);
}
const hud = document.querySelector('.hud');
if (hud) {
hud.classList.remove('hidden');
}
this.hudTimeout = setTimeout(() => {
if (this.hudVisible) {
const hud = document.querySelector('.hud');
if (hud) {
hud.classList.add('hidden');
}
}
}, constants_1.CONSTANTS.HUD_HIDE_TIMEOUT);
}
updateCanvasSize() {
const rect = this.canvas.getBoundingClientRect();
const dpr = window.devicePixelRatio || 1;
const width = Math.floor(rect.width * dpr);
const height = Math.floor(rect.height * dpr);
if (this.canvas.width !== width || this.canvas.height !== height) {
this.canvas.width = width;
this.canvas.height = height;
this.renderer.stop();
this.render();
}
}
updateHUD() {
const magElement = document.querySelector('.mag');
const coordsElement = document.querySelector('.coords');
const iterElement = document.querySelector('.iter');
if (magElement) {
const scale = Math.abs(this.state.scale);
const mag = 1 / scale;
magElement.textContent = `×${mag.toExponential(1)}`;
}
if (coordsElement) {
const centerX = this.state.reMin + (this.state.reMin + 2 * this.state.scale) / 2;
const centerY = this.state.imMax - (this.state.imMax - 2 * this.state.scale) / 2;
coordsElement.textContent = `(${centerX.toFixed(4)}, ${centerY.toFixed(4)})`;
}
if (iterElement) {
iterElement.textContent = `Iter: ${this.state.maxIter}`;
}
const fpsElement = document.querySelector('.fps');
if (fpsElement) {
fpsElement.textContent = `FPS: ${this.fps}`;
}
}
startFPSMonitor() {
let lastTime = performance.now();
let frameCount = 0;
const updateFPS = (currentTime) => {
frameCount++;
if (currentTime >= lastTime + 1000) {
this.fps = Math.round((frameCount * 1000) / (currentTime - lastTime));
frameCount = 0;
lastTime = currentTime;
this.updateHUD();
requestAnimationFrame(updateFPS);
}
else {
requestAnimationFrame(updateFPS);
}
};
requestAnimationFrame(updateFPS);
}
startAnimationLoop() {
const animate = (timestamp) => {
if (timestamp - this.lastFrameTime >= 1000 / 60) {
this.lastFrameTime = timestamp;
this.render();
}
this.animationId = requestAnimationFrame(animate);
};
requestAnimationFrame(animate);
}
render() {
if (this.renderer['isRunning']) {
// Cancel any existing render
this.renderer.stop();
}
// Calculate max iterations based on scale
const baseIter = constants_1.CONSTANTS.MAX_ITER_BASE;
const scaleFactor = Math.log10(1 / Math.abs(this.state.scale)) / Math.log10(1 / constants_1.CONSTANTS.MIN_SCALE);
const maxIter = Math.min(baseIter * Math.pow(constants_1.CONSTANTS.MAX_ITER_ZOOM_FACTOR, scaleFactor), 5000);
this.state.maxIter = Math.round(maxIter);
// Start progressive rendering
this.renderer.start(this.canvas, this.state.reMin, this.state.imMax, this.state.scale, this.state.palette, this.state.colorOffset, this.state.isJulia, this.state.cReal, this.state.cImag, this.state.maxIter, () => {
this.saveState();
this.updateHUD();
});
}
zoomToRegion(reMin, imMax, scale) {
// Add to history
this.addToHistory();
this.state.reMin = reMin;
this.state.imMax = imMax;
this.state.scale = scale;
this.render();
this.saveState();
this.resetHUDTimeout();
}
zoom(factor, centerX, centerY) {
// Calculate new scale
let newScale = this.state.scale * factor;
// Clamp to min/max scales
if (newScale > constants_1.CONSTANTS.MAX_SCALE)
newScale = constants_1.CONSTANTS.MAX_SCALE;
if (newScale < constants_1.CONSTANTS.MIN_SCALE)
newScale = constants_1.CONSTANTS.MIN_SCALE;
// Calculate new center coordinates
const oldScale = this.state.scale;
const dx = (centerX / this.canvas.width) * oldScale;
const dy = (-centerY / this.canvas.height) * oldScale;
const newReMin = this.state.reMin - dx * (newScale / oldScale - 1);
const newImMax = this.state.imMax + dy * (newScale / oldScale - 1);
this.addToHistory();
this.state.reMin = newReMin;
this.state.imMax = newImMax;
this.state.scale = newScale;
this.render();
this.saveState();
this.resetHUDTimeout();
}
addToHistory() {
const stateCopy = {
reMin: this.state.reMin,
imMax: this.state.imMax,
scale: this.state.scale,
palette: this.state.palette,
colorOffset: this.state.colorOffset,
isJulia: this.state.isJulia,
cReal: this.state.cReal,
cImag: this.state.cImag,
maxIter: this.state.maxIter,
};
this.history.push(stateCopy);
this.historyIndex = this.history.length - 1;
// Limit history size
if (this.history.length > 50) {
this.history.shift();
this.historyIndex--;
}
}
goBack() {
if (this.historyIndex > 0) {
this.historyIndex--;
this.state = { ...this.history[this.historyIndex] };
this.render();
this.saveState();
}
}
resetView() {
this.addToHistory();
this.state = {
reMin: -2.0,
imMax: 2.0,
scale: 3.0 / Math.min(this.canvas.width, this.canvas.height),
palette: this.palettes[constants_1.CONSTANTS.DEFAULT_PALETTE],
colorOffset: 0,
isJulia: false,
cReal: -0.8,
cImag: 0.156,
maxIter: constants_1.CONSTANTS.MAX_ITER_BASE,
};
this.render();
this.saveState();
}
toggleJuliaMode() {
this.addToHistory();
this.state.isJulia = !this.state.isJulia;
// If switching to Julia, use current center as c value
if (this.state.isJulia) {
const centerX = this.state.reMin + (this.state.reMin + 2 * this.state.scale) / 2;
const centerY = this.state.imMax - (this.state.imMax - 2 * this.state.scale) / 2;
this.state.cReal = centerX;
this.state.cImag = centerY;
}
this.render();
this.saveState();
}
updateActivePaletteButton(activePalette) {
document.querySelectorAll('.palette-btn').forEach(btn => {
btn.classList.toggle('active', btn.dataset.palette === activePalette);
});
}
// Public API for debugging and probing
iterationsAt(re, im) {
return this.state.isJulia
? this.state.maxIter // This would need to call the actual calculation
: this.state.maxIter; // Placeholder
}
setView(options) {
Object.assign(this.state, options);
this.render();
this.saveState();
}
destroy() {
this.renderer.stop();
if (this.animationId) {
cancelAnimationFrame(this.animationId);
}
if (this.hudTimeout) {
clearTimeout(this.hudTimeout);
}
if (this.resizeObserver) {
this.resizeObserver.disconnect();
}
window.removeEventListener('keydown', this.setupEventListeners);
}
}
exports.FractalExplorer = FractalExplorer;
// Initialize when the DOM is ready
document.addEventListener('DOMContentLoaded', () => {
const canvas = document.getElementById('fractalCanvas');
if (canvas) {
const explorer = new FractalExplorer(canvas);
// Expose API for debugging
window.fractal = {
iterationsAt: (re, im) => explorer.iterationsAt(re, im),
setView: (options) => explorer.setView(options),
};
}
});
};
// ── module: src/constants.ts ──
__mods["src/constants.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CONSTANTS = void 0;
exports.interpolateColor = interpolateColor;
exports.getColorFromPalette = getColorFromPalette;
// Constants and configuration for the fractal explorer.
exports.CONSTANTS = {
// Zoom speed (scale factor per wheel step)
ZOOM_SPEED: 1.15,
// Minimum and maximum zoom scales (magnification)
MIN_SCALE: 1e-6,
MAX_SCALE: 1e13,
// Base maximum iterations at default scale (1x)
MAX_ITER_BASE: 256,
// Iterations scale factor with zoom (more zoom = more iterations)
MAX_ITER_ZOOM_FACTOR: 2.0,
// Progressive rendering: number of refinement passes
PROGRESSIVE_PASSES: 4,
// Tile size for progressive rendering (width/height)
TILE_SIZE: 64,
// Palettes: each is an array of RGBA stops (0-255)
PALETTES: {
fire: [
{ offset: 0.0, color: [0, 0, 0, 255] }, // black interior
{ offset: 0.33, color: [255, 0, 0, 255] }, // red
{ offset: 0.66, color: [255, 128, 0, 255] }, // orange
{ offset: 1.0, color: [255, 255, 255, 255] }, // white
],
ocean: [
{ offset: 0.0, color: [0, 0, 0, 255] },
{ offset: 0.33, color: [0, 50, 100, 255] },
{ offset: 0.66, color: [100, 150, 255, 255] },
{ offset: 1.0, color: [255, 255, 255, 255] },
],
monochrome: [
{ offset: 0.0, color: [0, 0, 0, 255] },
{ offset: 0.5, color: [128, 128, 128, 255] },
{ offset: 1.0, color: [255, 255, 255, 255] },
],
psychedelic: [
{ offset: 0.0, color: [255, 0, 255, 255] }, // magenta
{ offset: 0.25, color: [0, 255, 255, 255] }, // cyan
{ offset: 0.5, color: [0, 255, 0, 255] }, // green
{ offset: 0.75, color: [255, 255, 0, 255] }, // yellow
{ offset: 1.0, color: [255, 0, 0, 255] }, // red
],
},
// Default active palette key
DEFAULT_PALETTE: 'fire',
// UI constants
HUD_HIDE_TIMEOUT: 3000, // ms before HUD auto-hides
};
// Helper to interpolate between two colors given a ratio (0-1)
function interpolateColor(c1, c2, t) {
return c1.map((v, i) => Math.round(v + (c2[i] - v) * t));
}
// Get color from palette at normalized iteration value (0-1)
function getColorFromPalette(palette, norm, offset = 0) {
if (norm <= 0)
return palette[0].color;
if (norm >= 1)
return palette[palette.length - 1].color;
// Apply offset (wrap-around)
let adjusted = (norm + offset) % 1;
if (adjusted < 0)
adjusted += 1;
// Find segment
for (let i = 0; i < palette.length - 1; i++) {
const p0 = palette[i];
const p1 = palette[i + 1];
if (adjusted >= p0.offset && adjusted <= p1.offset) {
const t = (adjusted - p0.offset) / (p1.offset - p0.offset);
return interpolateColor(p0.color, p1.color, t);
}
}
return [255, 255, 255, 255];
}
};
// ── module: src/render.ts ──
__mods["src/render.ts"] = function (exports, require, module) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProgressiveRenderer = void 0;
exports.smoothIteration = smoothIteration;
exports.calculateIterations = calculateIterations;
exports.renderTile = renderTile;
// Core math and rendering logic for the fractal explorer.
const constants_1 = require("./constants");
// Smooth coloring formula: n + 1 - log2(log|z|)
function smoothIteration(escapeTime, zReal, zImag) {
// z is the final iteration value before escape (complex number)
// Avoid division by zero for interior points
if (escapeTime >= constants_1.CONSTANTS.MAX_ITER_BASE) {
return constants_1.CONSTANTS.MAX_ITER_BASE; // interior point
}
const magnitude = zReal * zReal + zImag * zImag;
if (magnitude <= 0)
return escapeTime;
const logMagnitude = Math.log(magnitude) / Math.LN2;
return escapeTime + 1 - logMagnitude;
}
// Calculate escape iterations for a single point (c = re + i*im)
function calculateIterations(re, im, cReal, cImag, maxIter) {
let zReal = re;
let zImag = im;
let iter = 0;
while (iter < maxIter && zReal * zReal + zImag * zImag < 4) {
const temp = zReal * zReal - zImag * zImag + cReal;
zImag = 2 * zReal * zImag + cImag;
zReal = temp;
iter++;
}
return iter;
}
// Render a single tile (row range) of the fractal
function renderTile(imageData, startY, endY, reMin, imMax, scale, palette, colorOffset, isJulia, cReal, cImag, maxIter) {
const data = imageData.data;
const width = imageData.width;
const height = imageData.height;
for (let y = startY; y < endY; y++) {
const im = imMax - y * scale; // y axis inverted
for (let x = 0; x < width; x++) {
const re = reMin + x * scale;
let iterations;
if (isJulia) {
iterations = calculateIterations(re, im, cReal, cImag, maxIter);
}
else {
iterations = calculateIterations(re, im, re, im, maxIter);
}
let color;
if (iterations === maxIter) {
// Interior point
color = [0, 0, 0, 255];
}
else {
// Smooth coloring
const smooth = smoothIteration(iterations, re, im);
const norm = smooth / maxIter;
color = (0, constants_1.getColorFromPalette)(palette, norm, colorOffset);
}
const idx = (y * width + x) * 4;
data[idx] = color[0];
data[idx + 1] = color[1];
data[idx + 2] = color[2];
data[idx + 3] = color[3];
}
}
}
// Progressive rendering manager
class ProgressiveRenderer {
constructor() {
this.animationFrameId = null;
this.currentPass = 0;
this.tiles = [];
this.completedTiles = new Set();
this.imageData = null;
this.canvas = null;
this.isRunning = false;
}
start(canvas, reMin, imMax, scale, palette, colorOffset, isJulia, cReal, cImag, maxIter, onComplete) {
this.stop(); // cancel any existing render
this.canvas = canvas;
const ctx = canvas.getContext('2d');
this.imageData = ctx?.createImageData(canvas.width, canvas.height) ?? null;
this.onComplete = onComplete;
this.currentPass = 0;
this.completedTiles.clear();
this.isRunning = true;
// Initialize empty image with black background
if (this.imageData) {
const data = this.imageData.data;
for (let i = 0; i < data.length; i += 4) {
data[i] = 0;
data[i + 1] = 0;
data[i + 2] = 0;
data[i + 3] = 255;
}
}
this.scheduleNextPass(reMin, imMax, scale, palette, colorOffset, isJulia, cReal, cImag, maxIter);
}
scheduleNextPass(reMin, imMax, scale, palette, colorOffset, isJulia, cReal, cImag, maxIter) {
if (!this.isRunning)
return;
// Determine resolution for current pass based on pass number
const passIndex = this.currentPass;
const tileSize = constants_1.CONSTANTS.TILE_SIZE;
const width = this.canvas?.width || 0;
const height = this.canvas?.height || 0;
// For pass 0, use quarter resolution; for subsequent passes, increase resolution
const factor = Math.pow(2, Math.floor(passIndex / 2));
const renderWidth = Math.max(tileSize, Math.ceil(width / factor));
const renderHeight = Math.max(tileSize, Math.ceil(height / factor));
// Generate tiles for this pass
this.tiles = [];
for (let ty = 0; ty < height; ty += renderHeight) {
for (let tx = 0; tx < width; tx += renderWidth) {
// Only keep tiles that haven't been completed yet
const tileKey = `${tx},${ty}`;
if (!this.completedTiles.has(tileKey)) {
this.tiles.push({
x: tx,
y: ty,
width: Math.min(renderWidth, width - tx),
height: Math.min(renderHeight, height - ty),
});
}
}
}
// Render all tiles for this pass
this.renderPass(reMin, imMax, scale, palette, colorOffset, isJulia, cReal, cImag, maxIter);
}
renderPass(reMin, imMax, scale, palette, colorOffset, isJulia, cReal, cImag, maxIter) {
if (!this.imageData || this.tiles.length === 0) {
this.currentPass++;
if (this.currentPass < constants_1.CONSTANTS.PROGRESSIVE_PASSES) {
this.scheduleNextPass(reMin, imMax, scale, palette, colorOffset, isJulia, cReal, cImag, maxIter);
}
else {
this.complete();
}
return;
}
// Render tiles in batches to maintain responsiveness
const batchSize = Math.ceil(this.tiles.length / 4); // 4 batches per pass
const batch = this.tiles.splice(0, batchSize);
for (const tile of batch) {
// Calculate tile's region in the fractal space
const tileReMin = reMin + tile.x * scale;
const tileImMax = imMax - tile.y * scale;
const tileScale = scale;
// Render tile directly into the main image data
renderTile(this.imageData, tile.y, tile.y + tile.height, tileReMin, tileImMax, tileScale, palette, colorOffset, isJulia, cReal, cImag, maxIter);
// Mark tile as completed
const tileKey = `${tile.x},${tile.y}`;
this.completedTiles.add(tileKey);
}
// Update canvas with partial results
if (this.canvas && this.imageData) {
this.canvas.getContext('2d')?.putImageData(this.imageData, 0, 0);
}
// Schedule next batch of tiles for this pass
if (this.tiles.length > 0) {
this.animationFrameId = requestAnimationFrame(() => {
this.renderPass(reMin, imMax, scale, palette, colorOffset, isJulia, cReal, cImag, maxIter);
});
}
else {
// All tiles for this pass done, move to next pass
this.currentPass++;
if (this.currentPass < constants_1.CONSTANTS.PROGRESSIVE_PASSES) {
this.scheduleNextPass(reMin, imMax, scale, palette, colorOffset, isJulia, cReal, cImag, maxIter);
}
else {
this.complete();
}
}
}
stop() {
this.isRunning = false;
if (this.animationFrameId !== null) {
cancelAnimationFrame(this.animationFrameId);
this.animationFrameId = null;
}
this.tiles = [];
this.completedTiles.clear();
}
complete() {
this.isRunning = false;
this.animationFrameId = null;
this.tiles = [];
this.completedTiles.clear();
if (this.onComplete) {
this.onComplete();
}
}
}
exports.ProgressiveRenderer = ProgressiveRenderer;
};
// ── entry ──
__require("src/main.ts");
})();
</script>
</body>
</html>
<!-- agent-meta {"model":"unsloth/North-Mini-Code-1.0-GGUF","provider":"brigid","persona":"typescript","sessionId":"5cff54c3-01c3-4613-8aa0-88643fa03299","tokensIn":643744,"tokensOut":34145,"tokensTotal":677889,"cost":0,"turns":29,"toolCalls":28,"failedToolCalls":0,"timestamp":"2026-08-07T23:29:13.125Z"} -->