console
class ShaderPen {
constructor(shaderString, noRender) {
const ioTest = /\(\s*out\s+vec4\s+(\S+)\s*,\s*in\s+vec2\s+(\S+)\s*\)/;
const io = shaderString.match(ioTest);
shaderString = shaderString.replace('mainImage', 'main');
shaderString = shaderString.replace(ioTest, '()');
const uniforms = this.uniforms = {
iResolution: {
type: 'vec3',
value: [window.innerWidth, window.innerHeight, 0],
},
iTime: {
type: 'float',
value: 0,
},
iTimeDelta: {
type: 'float',
value: 0,
},
iFrame: {
type: 'int',
value: 0,
},
iMouse: {
type: 'vec4',
value: [0, 0, 0, 0],
},
};
shaderString = (io ? `#define ${io[1]} gl_FragColor\n#define ${io[2]} gl_FragCoord.xy\n` : '') + shaderString;
shaderString = Object.keys(uniforms)
.map((key) => ({
name: key,
type: uniforms[key].type,
}))
.reduce((a, uniform) => (
a + `uniform ${uniform.type} ${uniform.name};\n`
), '') + shaderString;
shaderString = 'precision highp float;\n' + shaderString;
const canvas = this.canvas = document.createElement('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.style.position = 'fixed';
canvas.style.left = 0;
canvas.style.top = 0;
document.body.append(canvas);
const gl = this.gl = canvas.getContext('webgl');
gl.clearColor(0, 0, 0, 0);
const vertexShader = this.vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, `
attribute vec2 position;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
}
`);
gl.compileShader(vertexShader);
const fragmentShader = this.fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, shaderString);
gl.compileShader(fragmentShader);
const program = this.program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
const vertices = this.vertices = new Float32Array([
-1, 1, 1, 1, 1, -1,
-1, 1, 1, -1, -1, -1,
]);
const buffer = this.buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
gl.useProgram(program);
program.position = gl.getAttribLocation(program, 'position');
gl.enableVertexAttribArray(program.position);
gl.vertexAttribPointer(program.position, 2, gl.FLOAT, false, 0, 0);
Object.keys(uniforms).forEach((key, i) => {
uniforms[key].location = gl.getUniformLocation(program, key);
});
this.reportErrors();
this._bind(
'mouseDown',
'mouseMove',
'mouseUp',
'render',
'resize'
);
window.addEventListener('mousedown', this.mouseDown);
window.addEventListener('mousemove', this.mouseMove);
window.addEventListener('mouseup', this.mouseUp);
window.addEventListener('resize', this.resize);
if (noRender !== 'NO_RENDER') {
this.render();
}
}
_bind(...methods) {
methods.forEach((method) => this[method] = this[method].bind(this));
}
mouseDown(e) {
this.mousedown = true;
this.uniforms.iMouse.value[2] = e.clientX;
this.uniforms.iMouse.value[3] = e.clientY;
}
mouseMove(e) {
if (this.mousedown) {
this.uniforms.iMouse.value[0] = e.clientX;
this.uniforms.iMouse.value[1] = e.clientY;
}
}
mouseUp(e) {
this.mousedown = false;
this.uniforms.iMouse.value[2] = 0;
this.uniforms.iMouse.value[3] = 0;
}
render(timestamp) {
const gl = this.gl;
let delta = this.lastTime ? ((timestamp - this.lastTime) / 1000) : 0;
this.lastTime = timestamp;
this.uniforms.iTime.value += delta;
this.uniforms.iTimeDelta.value = delta;
this.uniforms.iFrame.value++;
gl.clear(gl.COLOR_BUFFER_BIT);
Object.keys(this.uniforms).forEach((key) => {
const t = this.uniforms[key].type;
const method = t.match(/vec/) ? `${t[t.length - 1]}fv` : `1${t[0]}`;
gl[`uniform${method}`](this.uniforms[key].location, this.uniforms[key].value);
});
gl.drawArrays(gl.TRIANGLES, 0, this.vertices.length / 2);
requestAnimationFrame(this.render);
}
reportErrors() {
const gl = this.gl;
if (!gl.getShaderParameter(this.vertexShader, gl.COMPILE_STATUS)) {
console.log(gl.getShaderInfoLog(this.vertexShader));
}
if (!gl.getShaderParameter(this.fragmentShader, gl.COMPILE_STATUS)) {
console.log(gl.getShaderInfoLog(this.fragmentShader));
}
if (!gl.getProgramParameter(this.program, gl.LINK_STATUS)) {
console.log(gl.getProgramInfoLog(this.program));
}
}
resize() {
this.canvas.width = this.uniforms.iResolution.value[0] = window.innerWidth;
this.canvas.height = this.uniforms.iResolution.value[1] = window.innerHeight;
this.gl.viewport(0, 0, this.canvas.width, this.canvas.height);
}
}
new ShaderPen(`
#define AA 2
vec3 shape( in vec2 uv )
{
float time = iTime*0.05 + 47.0;
vec2 z = -1.0 + 2.0*uv;
z *= 1.5;
vec3 col = vec3(1.0);
for( int j=0; j<48; j++ )
{
float s = float(j)/16.0;
float f = 0.2*(0.5 + 1.0*fract(sin(s*20.0)));
vec2 c = 0.5*vec2( cos(f*time+17.0*s),sin(f*time+19.0*s) );
z -= c;
float zr = length( z );
float ar = atan( z.y, z.x ) + zr*0.6;
z = vec2( cos(ar), sin(ar) )/zr;
z += c;
col -= 0.5*exp( -10.0*dot(z,z) )* (0.25+0.4*sin( 5.5 + 1.5*s + vec3(1.6,0.8,0.5) ));
}
return col;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
float e = 1.0/iResolution.x;
vec3 tot = vec3(0.0);
for( int m=0; m<AA; m++ )
for( int n=0; n<AA; n++ )
{
vec2 uv = (fragCoord+vec2(m,n)/float(AA))/iResolution.xy;
vec3 col = shape( uv );
float f = dot(col,vec3(0.333));
vec3 nor = normalize( vec3( dot(shape(uv+vec2(e,0.0)),vec3(0.333))-f,
dot(shape(uv+vec2(0.0,e)),vec3(0.333))-f,
e ) );
col += 0.2*vec3(1.0,0.9,0.5)*dot(nor,vec3(0.8,0.4,0.2));;
col += 0.3*nor.z;
tot += col;
}
tot /= float(AA*AA);
tot = pow( clamp(tot,0.0,1.0), vec3(0.8,1.1,1.3) );
vec2 uv = fragCoord/iResolution.xy;
tot *= 0.4 + 0.6*pow( 16.0*uv.x*uv.y*(1.0-uv.x)*(1.0-uv.y), 0.1 );
fragColor = vec4( tot, 1.0 );
}
`);
<body></body>
html,body{border:none;padding:0;margin:0;}
#container {position:absolute;width:100%;height:100%;}