Skip to main content
Computer Science Framogen

Framogen

Fractal Mountain Generator using Diamond-Square algorithm

Controls
Generation
Terrain
Display

Space/R: regenerate | A: animate
V: toggle 3D | +/-: zoom | 0: reset
Mouse wheel: zoom | Drag: pan

Diamond-Square Algorithm

The Diamond-Square algorithm is a fractal terrain generation method that creates realistic heightmaps through recursive subdivision.

Steps:

  1. Initialize corners of a square grid with random values
  2. Diamond step: For each square, set its center to the average of its 4 corners + random offset
  3. Square step: For each diamond, set its center to the average of its 4 points + random offset
  4. Reduce the random offset (roughness decay)
  5. Repeat until all grid points are filled

Complexity: O(n²) where n = grid size

Color mapping: Heights are normalized to [0,1] and mapped to terrain colors (ocean → sea → beach → grass → forest → rocks → snow).

© 2013 - 2026 Cylian 🤖 Claude
Instructions Claude

Prompt utilisé pour régénérer cette page :

Page: Framogen
Description: "Fractal Mountain Generator using Diamond-Square algorithm"
Category: computer-science
Icon: terrain
Tags: fractal, procedural, terrain
Status: (none specified — no status field in front matter)

Front matter (index.md):
  title: "Framogen"
  description: "Fractal Mountain Generator using Diamond-Square algorithm"
  icon: "terrain"
  tags: ["fractal", "procedural", "terrain"]

NOTE: No status field in front matter (unlike other pages).

HTML structure (index.md):
  <section class="framogen-container container" style="margin: 0 auto;">
    <canvas id="framogen-canvas"></canvas>
  </section>

NOTE: Does NOT use .visual, .size-800, .ratio-1-1, or .canvas-contain classes.
Container class is "framogen-container container" with inline margin auto.
Canvas resizes dynamically to match grid resolution.

Widget files:
- _controls.right.md (weight: 10):
  ##### Controls — div.framogen-controls:
    {{< button id="generate-btn" label="Generate" >}}

- _options.right.md (weight: 20): Three sections using <dl> elements:
  ##### Generation — <dl> with:
    Seed: <input type="number" id="seed" value="0" style="width: 100%;">
    Noise: <select id="noise-type"> with Diamond-Square (selected) and Perlin
    Octaves: <input type="range" id="octaves" min="1" max="8" value="1">
    Detail: <select id="grid-size"> with Low 129x129 (7), Medium 257x257 (8), High 513x513 (9, selected), Ultra 1025x1025 (10)
  ##### Terrain — <dl> with:
    Roughness: range id="roughness" (20-100, value 50)
    Chaos: range id="chaos" (10-100, value 50)
    Deviation: range id="deviation" (10-100, value 50)
    Erosion: range id="erosion" (0-50, value 0)
  ##### Display — <dl> with:
    Shading: <select id="shading"> with Slope (selected) and Flat
    Retro 16 colors: <input type="checkbox" id="retro">
    3D Isometric: <input type="checkbox" id="view-3d">
  <p class="hint"> with keyboard shortcuts: Space/R, A, V, +/-, 0, mouse wheel, drag

- _algo.after.md (weight: 10): ##### Diamond-Square Algorithm — explains the 5 steps (initialize, diamond, square, reduce, repeat), complexity O(n²), color mapping description.

NOTE: The algorithm widget file is named _algo.after.md (not _algorithm.after.md).

Architecture (single file default.js):
- IIFE, no imports (uses console.log)
- Inspired by original Atari ST FRAMOGEN by Mocha/Mega1 (1992)

NOTE: No SCSS file. No default.scss exists for this page.

Configuration defaults:
  gridPower: 9 (grid size = 2^9 + 1 = 513)
  initialRoughness: 1.0, ROUGHNESS_DECAY: 0.5
  chaos: 0.5, deviation: 0.5, slopeShading: true
  currentSeed: Date.now(), noiseType: 'diamond', octaves: 1
  erosionIterations: 0, retroMode: false, view3D: false
  zoomLevel: 1.0, panX: 0, panY: 0

Seeded RNG (Mulberry32):
  seedRandom(seed): initializes rngState from seed
  seededRandom(): returns deterministic pseudo-random 0-1
  random(): alias for seededRandom()

Diamond-Square algorithm:
  createHeightMap(size, level, chaosAmount): Float32Array, corners initialized with level ± chaos
  diamondStep(map, size, x, y, half, roughness, deviationScale): avg of 4 square corners + random offset × roughness × deviation
  squareStep(map, size, x, y, half, roughness, deviationScale): avg of up to 4 diamond neighbors (edge-aware) + random offset
  diamondSquare(map, size, deviationScale): iterative subdivision, halving step and reducing roughness by ROUGHNESS_DECAY (0.5) each level
  normalizeHeightMap(map): scales all values to 0-1 range

Perlin noise (inline 2D implementation):
  PERM: Uint8Array(512) permutation table, GRAD: 8 gradient vectors
  initPerlin(): shuffle permutation table using seeded random
  fade(t): 6t^5 - 15t^4 + 10t^3
  lerp(a, b, t): linear interpolation
  grad(hash, x, y): gradient dot product
  perlin2D(x, y): standard 2D Perlin noise
  generatePerlinMap(size, numOctaves): generates height map with octave summation (halving amplitude, doubling frequency)

Hydraulic erosion (applyErosion):
  Droplet-based simulation: inertia=0.05, capacity=4, deposition=0.3, erosion=0.3, evaporation=0.01
  Each drop: random position, follows gradient, erodes downhill, deposits uphill or when over capacity
  Max 64 steps per drop, speed and water tracked
  erosionIterations * 100 = actual drop count

Terrain generation (generateTerrain):
  If noiseType === 'perlin': use generatePerlinMap with octaves
  If noiseType === 'diamond': Diamond-Square base terrain, normalized
    If octaves > 1: add detail layers with decreasing amplitude (×0.5 each octave)
  normalizeHeightMap on final result

Color system:
  7 elevation bands (ELEVATION_COLORS):
    0.20: deep ocean (#001f3f), 0.35: sea (#0074D9), 0.40: sand (#FFDC00),
    0.55: grass (#2ECC40), 0.70: forest (#1a5c1a), 0.85: rock (#808080), 1.00: snow (#FFFFFF)
  14 retro bands (RETRO_COLORS): Atari ST style from dark blue to white
  getInterpolatedColor(elevation): smooth interpolation between bands in normal mode, hard bands in retro mode
  hexToRgb(hex): parse hex color to {r, g, b}
  applySlopeShading(color, slope): factor = 1 + clamp(slope, -0.4, 0.4), multiplied to RGB

Slope calculation:
  calculateSlope(map, size, x, y): light from top-left
  slope = (h - hLeft) + (h - hUp) scaled by ×2

Rendering:
  draw2D(): pixel-by-pixel via ImageData, with zoom/pan coordinate mapping
    For each pixel: map to grid coords, get elevation, get interpolated color, apply slope shading
  draw3D(): isometric retro-style view
    Sky blue background (#87CEEB)
    Sample every N points (max(2, gridSize/128))
    Isometric projection: isoX = w/2 + (gx-gy)*scaledWidth, isoY = baseY - elevation*100
    Draw vertical lines from base to peak + top pixel
    Back-to-front rendering order

Canvas setup:
  Canvas resolution matches grid size exactly (not fixed 800px)
  Container sized to grid dimensions, aspect-ratio: unset
  NO devicePixelRatio scaling (1:1 pixel mapping for terrain)

Zoom/Pan:
  zoomLevel: 1.0 to 8.0
  panX, panY: offset in canvas pixels
  Mouse wheel: zoom toward cursor position (adjusts pan for centered zoom)
  Mouse drag (left button, only when zoomed): pan by delta / zoomLevel
  Keyboard: +/- zoom, 0 reset

Animation (morph):
  startAnimation(): store current as sourceMap, generate new targetMap
  animateStep(): interpolate heightMap = source + (target-source)*progress
  morphProgress += 0.02 per frame, completes at 1.0
  Triggered by 'A' key or animate button (if it existed in controls)

Keyboard shortcuts:
  Space/KeyR: regenerate with new seed (Date.now())
  KeyA: start animation morph
  KeyV: toggle 3D view
  Equal/NumpadAdd: zoom in (×1.5, max 8)
  Minus/NumpadSubtract: zoom out (÷1.5, min 1)
  Digit0: reset zoom/pan

Canvas click: regenerate when not zoomed and not dragging

Controls event listeners:
  generate-btn: new seed + generate()
  roughness slider: initialRoughness = value/50, regenerate
  chaos slider: chaos = value/100, regenerate
  deviation slider: deviation = value/100, regenerate
  grid-size select: gridPower = value, regenerate
  shading select: slopeShading = (value === 'slope'), redraw
  seed input: currentSeed = value, regenerate
  noise-type select: noiseType = value, regenerate
  octaves slider: octaves = value, regenerate
  erosion slider: erosionIterations = value, regenerate
  retro checkbox: retroMode = checked, redraw (with click stopPropagation)
  view-3d checkbox: view3D = checked, redraw (with click stopPropagation)

NOTE: animate-btn is referenced in JS but not present in the controls widget. Animation is only triggered via keyboard (A key).

Auto-init on DOM ready (DOMContentLoaded or immediate if already loaded).
Generates terrain immediately on initialization.

Page entièrement générée et maintenue par IA, sans intervention humaine.