<canvas id="flowers-pattern"></canvas>
<script type="x-shader/x-fragment" id="vertShader">
precision mediump float;
varying vec2 vUv;
attribute vec2 a_position;
void main() {
vUv = .5 * (a_position + 1.);
gl_Position = vec4(a_position, 0.0, 1.0);
}
</script>
<script type="x-shader/x-fragment" id="fragShader">
precision mediump float;
varying vec2 vUv;
uniform float u_scale;
uniform float u_time;
uniform float u_ratio;
uniform float u_petals_number;
uniform float u_flower_thickness;
uniform float u_def_floating;
uniform float u_visibility;
uniform float u_back_darkness;
uniform vec2 u_pointer;
#define TWO_PI 6.28318530718
float get_dot_shape(vec2 uv, vec2 center, float pwr) {
float pointer_shape = 1. - length(uv - center);
pointer_shape = clamp(pointer_shape, 0., 1.);
pointer_shape = pow(pointer_shape, pwr);
return pointer_shape;
}
// pseudo-random
float random(vec2 seed) {
return fract(sin(dot(seed, vec2(12.9898, 78.233))) * 43758.5453);
}
vec2 hash(vec2 p) {
p = vec2(dot(p, vec2(127.1, 311.7)), dot(p, vec2(269.5, 183.3)));
return fract(sin(p)*18.5453);
}
// polynomial-based smooth minimum
float smin(float a, float b, float k) {
float h = clamp(.5 + .5 * (b - a) / k, 0., 1.);
return mix(b, a, h) - k * h * (1. - h);
}
vec3 get_flower_pattern(vec2 uv, float scale, float tile_time, float rotation_time, float pointer) {
vec2 _uv = uv * scale;
vec2 i_uv = floor(_uv);
vec2 f_uv = fract(_uv);
float randomizer = 1.;
vec3 d = vec3(1.);
float cell_angle = 0.;
for (int y = -1; y <= 1; y++) {
for (int x = -1; x <= 1; x++) {
vec2 tile_offset = vec2(float(x), float(y));
vec2 o = hash(i_uv + tile_offset);
tile_offset += (.3 + .3 * sin(tile_time + TWO_PI * o)) - f_uv;
float dist = dot(tile_offset, tile_offset);
float old_min_dist = d.x;
d.z = max(d.x, max(d.y, min(d.z, dist)));// 3rd
d.y = max(d.x, min(d.y, dist));// 2nd
d.x = min(d.x, dist);// Closest
if (old_min_dist > d.x) {
cell_angle = atan(tile_offset.x, tile_offset.y);
randomizer = o.x;
}
}
}
d = sqrt(d);
float voronoi_shape = min(smin(d.z, d.y, .1) - d.x, 1.);
voronoi_shape *= u_flower_thickness;
voronoi_shape = pow(voronoi_shape, .3);
cell_angle += randomizer * rotation_time;
cell_angle += .7 * pointer;
cell_angle = mod(cell_angle, TWO_PI);
float sectoral_shape = abs(sin(cell_angle * .5 * (u_petals_number + floor(randomizer * 2.))));
sectoral_shape = mix(1., sectoral_shape, pow(d.x, .4));
sectoral_shape *= voronoi_shape;
float drawing_border = .3;
float border_shape = smoothstep(drawing_border, drawing_border + .05, sectoral_shape) - smoothstep(drawing_border - .2, drawing_border, sectoral_shape);
sectoral_shape = smoothstep(drawing_border, drawing_border + .1, sectoral_shape);
float mid_shape = 1. - smoothstep(.05, .07, d.x - .03 * pointer);
float visibility = step(randomizer, u_visibility);
border_shape *= visibility;
sectoral_shape *= visibility;
mid_shape *= visibility;
vec3 color = vec3(.1, .1, .2) / (1. + u_back_darkness);
color = mix(color, vec3(.8, .82, .87), sectoral_shape);
color += mix(color, vec3(.8, .1, .1), border_shape);
color = mix(color, vec3(1., .2 + .5 * randomizer, .2), mid_shape);
return color;
}
void main() {
vec2 uv = vUv;
uv.x *= u_ratio;
vec2 point = u_pointer;
point.x *= u_ratio;
float pointer_shape = get_dot_shape(uv, point, 4.);
float tile_floating_speed = .0004 * u_time * u_def_floating;
float flower_rotation_speed = .0002 * u_time;
vec3 color = get_flower_pattern(uv, u_scale, tile_floating_speed, flower_rotation_speed, pointer_shape);
float opacity = 1.;
gl_FragColor = vec4(color, opacity);
}
</script>
body, html {
margin: 0;
padding: 0;
overflow: hidden;
}
canvas#flowers-pattern {
display: block;
width: 100%;
}
.lil-gui {
--width: 450px;
max-width: 90%;
--widget-height: 20px;
font-size: 15px;
--input-font-size: 15px;
--padding: 10px;
--spacing: 10px;
--slider-knob-width: 5px;
--background-color: rgba(5, 0, 15, .8);
--widget-color: rgba(255, 255, 255, .3);
--focus-color: rgba(255, 255, 255, .4);
--hover-color: rgba(255, 255, 255, .5);
--font-family: monospace;
}
.lil-gui.autoPlace {
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
import GUI from 'https://cdn.jsdelivr.net/npm/lil-gui@0.18.2/+esm'
const canvasEl = document.querySelector("#flowers-pattern");
const mouseThreshold = .6;
const devicePixelRatio = Math.min(window.devicePixelRatio, 2);
const mouse = {
x: -.5 * window.innerWidth,
y: .5 * window.innerHeight,
tX: -.5 * window.innerWidth,
tY: .5 * window.innerHeight,
}
const params = {
scale: 7,
petalsNumber: 5,
flowerThickness: .4,
floatingSpeed: 1,
visibility: .75,
backDarkness: .2,
}
let uniforms;
const gl = initShader();
createControls();
render();
window.addEventListener("resize", resizeCanvas);
resizeCanvas();
window.addEventListener("mousemove", e => {
updateMousePosition(e.pageX, e.pageY);
});
window.addEventListener("touchmove", e => {
updateMousePosition(e.targetTouches[0].pageX, e.targetTouches[0].pageY);
});
canvasEl.addEventListener("click", e => {
updateMousePosition(e.pageX, e.pageY);
});
function updateMousePosition(eX, eY) {
mouse.tX = eX;
mouse.tY = eY;
}
function initShader() {
const vsSource = document.getElementById("vertShader").innerHTML;
const fsSource = document.getElementById("fragShader").innerHTML;
const gl = canvasEl.getContext("webgl") || canvasEl.getContext("experimental-webgl");
if (!gl) {
alert("WebGL is not supported by your browser.");
}
function createShader(gl, sourceCode, type) {
const shader = gl.createShader(type);
gl.shaderSource(shader, sourceCode);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error("An error occurred compiling the shaders: " + gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
}
const vertexShader = createShader(gl, vsSource, gl.VERTEX_SHADER);
const fragmentShader = createShader(gl, fsSource, gl.FRAGMENT_SHADER);
function createShaderProgram(gl, vertexShader, fragmentShader) {
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error("Unable to initialize the shader program: " + gl.getProgramInfoLog(program));
return null;
}
return program;
}
const shaderProgram = createShaderProgram(gl, vertexShader, fragmentShader);
uniforms = getUniforms(shaderProgram);
function getUniforms(program) {
let uniforms = [];
let uniformCount = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
for (let i = 0; i < uniformCount; i++) {
let uniformName = gl.getActiveUniform(program, i).name;
uniforms[uniformName] = gl.getUniformLocation(program, uniformName);
}
return uniforms;
}
const vertices = new Float32Array([-1., -1., 1., -1., -1., 1., 1., 1.]);
const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
gl.useProgram(shaderProgram);
const positionLocation = gl.getAttribLocation(shaderProgram, "a_position");
gl.enableVertexAttribArray(positionLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
gl.uniform1f(uniforms.u_scale, params.scale);
gl.uniform1f(uniforms.u_petals_number, params.petalsNumber);
gl.uniform1f(uniforms.u_flower_thickness, params.flowerThickness);
gl.uniform1f(uniforms.u_def_floating, params.floatingSpeed);
gl.uniform1f(uniforms.u_visibility, params.visibility);
gl.uniform1f(uniforms.u_back_darkness, params.backDarkness);
return gl;
}
function render() {
const currentTime = performance.now();
gl.uniform1f(uniforms.u_time, currentTime);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
mouse.x += (mouse.tX - mouse.x) * mouseThreshold;
mouse.y += (mouse.tY - mouse.y) * mouseThreshold;
gl.uniform2f(uniforms.u_pointer, mouse.x / window.innerWidth, 1. - mouse.y / window.innerHeight);
requestAnimationFrame(render);
}
function resizeCanvas() {
canvasEl.width = window.innerWidth * devicePixelRatio;
canvasEl.height = window.innerHeight * devicePixelRatio;
gl.viewport(0, 0, canvasEl.width, canvasEl.height);
gl.uniform1f(uniforms.u_ratio, canvasEl.width / canvasEl.height);
}
function createControls() {
const gui = new GUI();
gui.add(params, "scale", 5, 15, .01)
.name("scale")
.onChange(v => {
gl.uniform1f(uniforms.u_scale, v);
});
gui.add(params, "petalsNumber", 4, 8, 1)
.name("petals number")
.onChange(v => {
gl.uniform1f(uniforms.u_petals_number, v);
});
gui.add(params, "flowerThickness", .2, .8)
.name("flower thickness")
.onChange(v => {
gl.uniform1f(uniforms.u_flower_thickness, v);
});
gui.add(params, "floatingSpeed", .5, 3, .5)
.name("floating speed")
.onChange(v => {
gl.uniform1f(uniforms.u_def_floating, v);
});
gui.add(params, "visibility", .1, 1, .01)
.onChange(v => {
gl.uniform1f(uniforms.u_visibility, v);
});
gui.add(params, "backDarkness", 0, 1, .01)
.name("background darkness")
.onChange(v => {
gl.uniform1f(uniforms.u_back_darkness, v);
});
}
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.