Last active
October 4, 2022 12:44
-
-
Save onetdev/0d57891b02faf9805aceb1f3c82912d0 to your computer and use it in GitHub Desktop.
3D noise map slice render supporting
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { createNoise3D } from 'simplex-noise'; | |
// You will need an HTML with a `<cavas id="canvas" />` and `<span id='fps'>` and this ts file. | |
// This script only uses speed and doesn't care about frame draw time delta. | |
// Samples: | |
// - https://www.youtube.com/watch?v=ApI1m78MvAQ | |
// - https://www.youtube.com/watch?v=TPkWzFi1DrA | |
// - https://www.youtube.com/shorts/g-l_s-GkMCw (this is my favorite) | |
const config = { | |
// Clamping range (the smaller the value, the thinner the lines will be) | |
precission: 0.01, | |
// Slice distance per frame, lower number will result in smoother experience | |
speed: 0.01, | |
// Draw using hue wheel noise map or solid white | |
hueMode: false, | |
// Pixel shift/offset previous frame on Y axis each frame | |
decayShiftY: 0, | |
// Pixel shift/offset previous frame on X axis each frame | |
decayShiftX: 0, | |
// Fading out previous frame, use 0 to turn it off (esentially keeping previous frame and drawing on top of it) | |
decay: 0.05, | |
}; | |
(async () => { | |
const canvas = document.getElementById('canvas') as HTMLCanvasElement | null; | |
const fpsCounter = document.getElementById('fps') as HTMLSpanElement | null; | |
if (!canvas || !fpsCounter) { | |
return console.error('Canvas or countet span not found'); | |
} | |
const ctx = canvas.getContext('2d'); | |
if (!ctx) { | |
console.error('Missing canvas context'); | |
return; | |
} | |
ctx.canvas.width = window.innerWidth; | |
ctx.canvas.height = window.innerHeight; | |
const windowResize = () => { | |
ctx.canvas.width = window.innerWidth; | |
ctx.canvas.height = window.innerHeight; | |
}; | |
window.addEventListener('resize', windowResize); | |
let time = 0; | |
const spaceNoise = createNoise3D(); | |
const hueNoise = createNoise3D(); | |
const clampMin = 1 - config.precission; | |
const clampMax = 1 + config.precission; | |
const draw = () => { | |
const start = new Date().getTime(); | |
const width = ctx.canvas.width; | |
const height = ctx.canvas.height; | |
if (config.decayShiftX > 0 || config.decayShiftY > 0 ) { | |
const prevFrame = ctx.getImageData(0, 0, width, height); | |
ctx.putImageData(prevFrame, config.decayShiftX, config.decayShiftY); | |
} | |
if (config.decay > 0) { | |
ctx.fillStyle = `rgba(0,0,0,${config.decay})`; | |
ctx.fillRect(0, 0, width, height); | |
} | |
for (let x = 0; x < width; x++) { | |
for (let y = 0; y < height; y++) { | |
const value = spaceNoise(x / width, y / height, time) + 1; | |
if (value < clampMin || value > clampMax) { | |
continue; | |
} | |
if (config.hueMode) { | |
const hue = hueNoise(x / width, y / height, time); | |
const color = Math.round((hue + 1) * 180); | |
ctx.fillStyle = `hsl(${color},100%,50%)`; | |
ctx.fillRect(x, y, 1, 1); | |
} else { | |
ctx.fillStyle = `#ffffff`; | |
ctx.fillRect(x, y, 1, 1); | |
} | |
} | |
} | |
// Estimated FPS based on frame draw, not actual FPS | |
fpsCounter.innerText = `${Math.round(1000 / (new Date().getTime() - start))} fps`; | |
time += config.speed; | |
window.requestAnimationFrame(draw); | |
}; | |
window.requestAnimationFrame(draw); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment