Files
drags-and-nerds/v0-appel-projet/public/main.js
T

201 lines
7.2 KiB
JavaScript

/*
To do:
Press z for zen mode (hides all control and other display on top of the canvas)
Ability to add this shader effect on top of an image?
Presets / seed choice??
Allow user to upload a song, and then it becomes audio reactive?
Generate perfect loops in x seconds
*/
// Initialize WebGL context
const canvas = document.getElementById('canvas');
let startingWidth = window.innerWidth;
let startingHeight = window.innerHeight;
canvas.width = startingWidth;
canvas.height = startingHeight;
console.log("canvas width/height: "+canvas.width+" / "+canvas.height);
const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
let isPlaying = false;
let animationID = null;
let randomSeed;
let time;
let timeOffset = 0;
// FPS tracking variables
let frameCount = 0;
let lastTime = 0;
let fps = 0;
if (!gl) {
alert('WebGL not supported');
}
// Compile shaders
function compileShader(source, type) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error('Shader compilation error:', gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
}
// Create program
const vertexShader = compileShader(document.getElementById('vertexShader').textContent, gl.VERTEX_SHADER);
const fragmentShader = compileShader(document.getElementById('fragmentShader').textContent, gl.FRAGMENT_SHADER);
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error('Program linking error:', gl.getProgramInfoLog(program));
}
gl.useProgram(program);
// Create rectangle covering the entire canvas
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-1.0, -1.0,
1.0, -1.0,
-1.0, 1.0,
1.0, 1.0
]), gl.STATIC_DRAW);
// Set up attributes and uniforms
const positionLocation = gl.getAttribLocation(program, 'position');
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
const timeLocation = gl.getUniformLocation(program, 'time');
const resolutionLocation = gl.getUniformLocation(program, 'resolution');
const seedLocation = gl.getUniformLocation(program, 'seed');
// GUI-controlled uniform locations
const timeScaleLocation = gl.getUniformLocation(program, 'timeScale');
const bloomStrengthLocation = gl.getUniformLocation(program, 'bloomStrength');
const saturationLocation = gl.getUniformLocation(program, 'saturation');
const grainAmountLocation = gl.getUniformLocation(program, 'grainAmount');
const colorTintLocation = gl.getUniformLocation(program, 'colorTint');
const minCircleSizeLocation = gl.getUniformLocation(program, 'minCircleSize');
const circleStrengthLocation = gl.getUniformLocation(program, 'circleStrength');
const distortXLocation = gl.getUniformLocation(program, 'distortX');
const distortYLocation = gl.getUniformLocation(program, 'distortY');
const patternAmpLocation = gl.getUniformLocation(program, 'patternAmp');
const patternFreqLocation = gl.getUniformLocation(program, 'patternFreq');
// Initialize parameters object for dat.gui
const params = {
canvasWidth: startingWidth,
canvasHeight: startingHeight,
timeScale: .666,
patternAmp: 2,
patternFreq: 0.4,
bloomStrength: 0.777,
saturation: 1.74,
grainAmount: 0.161,
colorTintR: 1.5,
colorTintG: 1.0,
colorTintB: 1.0,
minCircleSize: 2.8,
circleStrength: 0,
distortX: 1,
distortY: 1,
};
// Also refresh on page load
window.addEventListener('load', refreshPattern);
window.addEventListener('resize', updateCanvasSize);
// Initialize dat.gui
const gui = new dat.GUI({ autoplace: false });
gui.close();
// Add GUI controls with folders for organization
const canvasFolder = gui.addFolder('Canvas Size');
canvasFolder.add(params, 'canvasWidth', 100, 4000).step(10).name('Width').onChange(updateCanvasSize);
canvasFolder.add(params, 'canvasHeight', 100, 4000).step(10).name('Height').onChange(updateCanvasSize);
canvasFolder.open();
const timeFolder = gui.addFolder('Animation');
timeFolder.add(params, 'timeScale', 0.1, 3.0).name('Speed').onChange(updateUniforms);
timeFolder.open();
const patternFolder = gui.addFolder('Pattern');
patternFolder.add(params, 'patternAmp', 1.0, 50.0).step(0.1).name('Pattern Amp').onChange(updateUniforms);
patternFolder.add(params, 'patternFreq', 0.2, 10.0).step(0.1).name('Pattern Freq').onChange(updateUniforms);
patternFolder.open();
const visualFolder = gui.addFolder('Visual Effects');
visualFolder.add(params, 'bloomStrength', 0.0, 5.0).name('Bloom').onChange(updateUniforms);
visualFolder.add(params, 'saturation', 0.0, 2.0).name('Saturation').onChange(updateUniforms);
visualFolder.add(params, 'grainAmount', 0.0, 0.5).name('Grain').onChange(updateUniforms);
visualFolder.add(params, 'minCircleSize', 0.0, 10.0).name('Circle Size').onChange(updateUniforms);
visualFolder.add(params, 'circleStrength', 0.0, 3.0).name('Circle Strength').onChange(updateUniforms);
visualFolder.add(params, 'distortX', 0.0, 50.0).name('Distort-X').onChange(updateUniforms);
visualFolder.add(params, 'distortY', 0.0, 50.0).name('Distort-Y').onChange(updateUniforms);
visualFolder.open();
const colorFolder = gui.addFolder('Color Tint');
colorFolder.add(params, 'colorTintR', 0.0, 1.5).name('Red').onChange(updateUniforms);
colorFolder.add(params, 'colorTintG', 0.0, 1.5).name('Green').onChange(updateUniforms);
colorFolder.add(params, 'colorTintB', 0.0, 1.5).name('Blue').onChange(updateUniforms);
colorFolder.open();
// Function to update shader uniforms from GUI values
function updateUniforms() {
gl.uniform1f(timeScaleLocation, params.timeScale);
gl.uniform1f(patternAmpLocation, params.patternAmp);
gl.uniform1f(patternFreqLocation, params.patternFreq);
gl.uniform1f(bloomStrengthLocation, params.bloomStrength);
gl.uniform1f(saturationLocation, params.saturation);
gl.uniform1f(grainAmountLocation, params.grainAmount);
gl.uniform3f(colorTintLocation, params.colorTintR, params.colorTintG, params.colorTintB);
gl.uniform1f(minCircleSizeLocation, params.minCircleSize);
gl.uniform1f(circleStrengthLocation, params.circleStrength);
gl.uniform1f(distortXLocation, params.distortX);
gl.uniform1f(distortYLocation, params.distortY);
}
function drawScene(){
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
// Animation loop
function render(timestamp) {
if (isPlaying) {
// Calculate adjusted time by subtracting the offset
const adjustedTime = timestamp - timeOffset;
time = timestamp;
const timeInSeconds = adjustedTime * 0.0035;
gl.uniform1f(timeLocation, timeInSeconds);
gl.uniform2f(resolutionLocation, canvas.width, canvas.height);
// If video recording is ongoing, drawScene is called already
if (!recordVideoState || useMobileRecord) {
drawScene();
}
animationID = requestAnimationFrame(render);
}
}
// Start the animation loop
isPlaying = true;
refreshPattern();
updateUniforms();
animationID = requestAnimationFrame(render);