While trying to implement noise on a 2D shape, our experiments led us to create our first community generated function!

Big thanks to @mimetikmusic for helping us tease out the math needed to apply some noise to the edges of our shape function (aka distance function) in order to create some satisfyingly wiggly borders.

Henceforth known as the wiggly noise effect 🔥🔥🔥 — here is the function:

/* 
* WIGGLY
* - authored with the math help by @mimetikmusic -
* (cx) centerPoint in the x axis
* (cy) centerPoint in the y axis
* (freq) frequency of the wobble
* (mag) magnitude of the wobble
* (sp) spread of the wobble 
* returns => wiggly noise
*/

float wiggly(float cx, float cy, float mag, float freq, float sp) {
    float w = sin(cx * mag * freq * PI) * cos(cy * mag * freq * PI) * sp;
    return w;
}
<!--
 * #curiouslyminded
 * 
 * Follow:
 * https://www.twitch.tv/curiouslyminded
 * https://www.youtube.com/curiouslyminded
-->

<div id="example"></div> <script id="vertex" type="x-shader/x-vertex"> void main() { gl_Position = vec4(position, 1.0); } </script> <script id="fragment" type="x-shader/x-vertex"> precision mediump float; #define PI 3.14159265359 uniform vec2 u_resolution; uniform float u_time;

// WIGGLY // authored with the math help by @mimetikmusic - https://www.instagram.com/mimetikmusic/ // (cx) centerPoint in the x axis // (cy) centerPoint in the y axis // (freq) frequency of the wobble // (mag) magnitude of the wobble // (sp) spread of the wobble float wiggly(float cx, float cy, float mag, float freq, float sp) { float w = sin(cx * mag * freq * PI) * cos(cy * mag * freq * PI) * sp; return w; }

void main() { vec2 uv = (gl_FragCoord.xy - u_resolution * .5) / u_resolution.yy + 0.5; float circle = distance(uv, vec2(0.5)); circle += wiggly( uv.x + u_time * 0.05, uv.y - u_time * 0.05, 3., 6., 0.005 ); circle = step(0.02, abs(circle - 0.3)); vec3 color = vec3(1.0-circle); gl_FragColor = vec4(color, 1.0); } </script>

* {
user-select: none;
}

body {
height: 100vh;
background-color: black;
margin: 0;
padding: 0;
overflow: hidden;
position: relative;
}
/* 
 * #curiouslyminded
 *
 * This JS is an adapted template from the https://thebookofshaders.com/
 */

let camera, scene, renderer, clock;
let uniforms;

function init() {
	const container = document.getElementById("example");

	clock = new THREE.Clock();
	camera = new THREE.Camera();
	camera.position.z = 1;

	scene = new THREE.Scene();

	const geometry = new THREE.PlaneBufferGeometry(2, 2);

	uniforms = {
		u_time: { type: "f", value: 1.0 },
		u_resolution: { type: "v2", value: new THREE.Vector2() },
	};

	const material = new THREE.ShaderMaterial({
		uniforms,
		vertexShader: document.getElementById("vertex").textContent,
		fragmentShader: document.getElementById("fragment").textContent
	});

	const mesh = new THREE.Mesh(geometry, material);
	scene.add(mesh);

	renderer = new THREE.WebGLRenderer();
	renderer.setPixelRatio(window.devicePixelRatio);

	container.appendChild(renderer.domElement);

	onWindowResize();
	window.addEventListener("resize", onWindowResize);
}

function onWindowResize() {
	renderer.setSize(window.innerWidth, window.innerHeight);
	uniforms.u_resolution.value.x = renderer.domElement.width;
	uniforms.u_resolution.value.y = renderer.domElement.height;
}

function render() {
	uniforms.u_time.value = clock.getElapsedTime();
	renderer.render(scene, camera);
}

function animate() {
	render();
	requestAnimationFrame(animate);
}

init();
animate();