Show code
```html
<!DOCTYPE html>
<html>
<head><title>Infinite Noise Landscape</title></head>
<body style="margin:0; overflow:hidden;"><canvas id="c"></canvas><script>
const c = document.getElementById('c');
const ctx = c.getContext('2d');
let W, H;
let offsetX = 0;
function resize() {
W = window.innerWidth;
H = window.innerHeight;
c.width = W;
c.height = H;
}
window.addEventListener('resize', resize);
resize();
// Simple seeded pseudo-random number generator (for consistent noise generation)
let seed = Math.random() * 10000;
function rand(x, y) {
let s = x * 34527 + y * 59621 + seed;
s ^= s >>> 18;
return (s / 0x100000000);
}
// Interpolation function: smoothstep
function smooth(t) { return t * t * t * (t * (t * 6 - 15) + 10); }
// Perlin/Value Noise implementation for a single point (x, y)
function noise(x, y) {
let X = Math.floor(x);
let Y = Math.floor(y);
let xf = x - X;
let yf = y - Y;
// Interpolate 4 corners using randomized values
return (
smooth(xf) * (rand(X, Y) * (1 - smooth(yf)) + rand(X + 1, Y) * smooth(yf)) +
smooth(Math.min(xf, 1 - xf)) * (rand(X + 1, Y) * (1 - smooth(yf)) + rand(X, Y + 1) * smooth(yf))
) / 2; // Normalize the range slightly
}
// Fractional Brownian Motion (FBM) using multiple octaves
function fbm(x, y, scale, octaves, persistence, lacunarity) {
let total = 0;
let frequency = 1;
let amplitude = 1;
let maxValue = 0;
for (let i = 0; i < octaves; i++) {
total += noise(x * scale * frequency, y * scale * frequency) * amplitude;
maxValue += amplitude;
amplitude *= persistence;
frequency *= lacunarity;
}
return total / maxValue; // Normalize the result roughly to [-1, 1]
}
// Color mapping based on normalized height (0.0 to 1.0)
function getColor(h) {
h = Math.max(0, Math.min(1, h));
let r, g, b;
if (h < 0.2) { // Water (deep blue)
r = Math.floor(Math.sin(h * 5) * 50 + 100);
g = Math.floor(Math.sin(h * 3) * 50 + 80);
b = Math.floor(Math.cos(h * 2) * 50 + 120);
} else if (h < 0.35) { // Sand/Shallow Water (yellow-brown)
r = 255; g = Math.floor((h - 0.2) * 400); b = 0;
} else if (h < 0.5) { // Grass/Dirt (green)
r = Math.floor(100 + (h - 0.35) * 150); g = 180; b = Math.floor(50 + (h - 0.35) * 100);
} else if (h < 0.7) { // Rock/Stone (gray)
r = g = b = Math.floor(100 + (h - 0.5) * 200);
} else { // Snow/Peak (white)
r = g = b = Math.floor(255 * (h - 0.7) / 0.3);
}
return `rgb(${Math.min(255, r)}, ${Math.min(255, g)}, ${Math.min(255, b)})`;
}
function draw() {
ctx.clearRect(0, 0, W, H);
// Draw the world offset indicator (top left)
ctx.fillStyle = 'rgba(0, 0, 0, 0.6)';
ctx.fillRect(10, 10, 250, 30);
ctx.fillStyle = 'white';
ctx.font = '14px Arial';
ctx.fillText('Offset X: ' + offsetX.toFixed(Math.floor(offsetX % 100)) + ' px', 15, 28);
// Landscape rendering parameters
const SCALE = 0.003;
const OCTAVES = 6;
const PERSISTENCE = 0.5;
const LACUNARITY = 2.0;
for (let x = 0; x < W; x++) {
// Sample height at current X position, using global offset for seamless scroll
let h_normalized = fbm(
offsetX + x * 0.1 * SCALE, // Horizontal input uses the scroll offset
Math.floor(x / 50), // Vertical input is constant across a row but changes slowly over width
SCALE, OCTAVES, PERSISTENCE, LACUNARITY
);
// Map noise [-1, 1] range to height [0, H]
let height = h_normalized * 0.5 + 0.5; // Scale to [0, 1]
height *= (H - 20) / 2; // Max height scale
// Color the column slice
ctx.fillStyle = getColor(height);
ctx.fillRect(x, H - Math.min(height * W/W, H-20), 1, Math.max(1, height * W/W));
}
}
function animate() {
// Update offset smoothly (scroll speed)
offsetX += 1; // Controls horizontal scrolling speed (pixels per frame)
draw();
requestAnimationFrame(animate);
}
// Start the animation loop
animate();
</script></body></html>
```