Skip to content

Instantly share code, notes, and snippets.

@j-k-projects
Created May 8, 2020 01:20
Show Gist options
  • Save j-k-projects/5ab6f97c87d06e97dedb86e2c40b036e to your computer and use it in GitHub Desktop.
Save j-k-projects/5ab6f97c87d06e97dedb86e2c40b036e to your computer and use it in GitHub Desktop.
Shapeshifter Blob
<div class="canvas_wrapper">
<canvas id="c"></canvas>
</div>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<filter id="goo">
<feGaussianBlur in="SourceGraphic" result="blur" stdDeviation="8" />
<feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -7" result="joint" />
</filter>
</defs>
</svg>
console.clear()
class Utils {
static randomRange(min, max) {
return Math.random() * (max - min) + min
}
static mapRange (value, inputMin, inputMax, outputMin, outputMax, clamp) {
if (Math.abs(inputMin - inputMax) < Number.EPSILON) {
return outputMin;
} else {
var outVal = ((value - inputMin) / (inputMax - inputMin) * (outputMax - outputMin) + outputMin);
if (clamp) {
if (outputMax < outputMin) {
if (outVal < outputMax) outVal = outputMax;
else if (outVal > outputMin) outVal = outputMin;
} else {
if (outVal > outputMax) outVal = outputMax;
else if (outVal < outputMin) outVal = outputMin;
}
}
return outVal;
}
}
}
Utils.simplex = new SimplexNoise('seed')
class App {
constructor() {
this.canvas = document.getElementById('c')
this.ctx = this.canvas.getContext('2d')
this.shadowCanvas = document.createElement('canvas')
this.shadowCtx = this.shadowCanvas.getContext('2d')
this.timestamp = 0
this.fpsHistory = []
this.angle = 0
this.setUpVars()
this.setUpListeners()
// this.setUpGui()
this.update()
}
setUpGui() {
const pane = new Tweakpane()
const folder = pane.addFolder({
expanded: false,
title: 'Settings',
})
folder.addInput(this.config, 'bgColor')
}
setUpVars() {
this.canvas.width = this.shadowCanvas.width = this.wWidth = window.innerWidth
this.canvas.height = this.shadowCanvas.height = this.wHeight = window.innerHeight
this.wCenterX = this.wWidth / 2
this.wCenterY = this.wHeight / 2
this.wHypot = Math.hypot(this.wWidth, this.wHeight)
this.wMin = Math.min(this.wWidth, this.wHeight)
}
setUpListeners() {
window.addEventListener('resize', this.setUpVars.bind(this))
}
drawTheThing(ctx, angle, scale = 1, color = '#000') {
const angleStep = Math.PI * 0.01
const maxRadius = this.wMin * 0.3 * scale
let radius = maxRadius
ctx.save()
ctx.translate(this.wCenterX, this.wCenterY)
ctx.rotate(this.angle + angle)
ctx.beginPath()
const startR = Utils.simplex.noise2D(Math.sin(0), this.timestamp) * radius
const startX = Math.sin(0) * startR
const startY = Math.cos(0) * startR
ctx.moveTo(startX, startY)
for (let angle = 0; angle <= Math.PI * 2; angle += angleStep) {
const n = Utils.simplex.noise2D(Math.sin(angle), this.timestamp)
const newRadius = n * radius
const xPos = Math.sin(angle) * newRadius
const yPos = Math.cos(angle) * newRadius
ctx.lineTo(
xPos,
yPos
)
}
ctx.fillStyle = color
ctx.fill()
for (let angle = 0; angle <= Math.PI * 2; angle += angleStep) {
const n = Utils.simplex.noise2D(Math.sin(angle), this.timestamp)
const newRadius = Math.pow(n * radius, 1.1)
const xPos = Math.sin(angle) * newRadius
const yPos = Math.cos(angle) * newRadius
ctx.beginPath()
ctx.arc(
xPos,
yPos,
this.wMin * 0.01,
0, Math.PI * 2
)
ctx.fill()
}
ctx.restore()
}
draw(ctx) {
ctx.save()
ctx.clearRect(0, 0, this.wWidth, this.wHeight)
ctx.restore()
this.drawTheThing(ctx, 0)
this.drawTheThing(ctx, Math.PI * 0.13, 0.9)
this.drawTheThing(ctx, Math.PI * 0.24, 0.8)
this.drawTheThing(ctx, Math.PI * 0.37, 0.8)
this.drawTheThing(ctx, Math.PI * 0.6, 0.8)
this.drawTheThing(ctx, Math.PI * 0.77, 1)
}
update(t) {
const prevTimestamp = this.timestamp * 5000
if (t) {
this.timestamp = t / 5000
this.angle += 0.001
this.draw(this.shadowCtx)
}
this.ctx.clearRect(0, 0, this.wWidth, this.wHeight)
this.ctx.drawImage(this.shadowCanvas, 0, 0)
// show fps
const fps = Math.round(1 / (t - prevTimestamp) * 1000)
this.fpsHistory.unshift(fps)
this.fpsHistory.length = 5
this.ctx.font = '16px sans-serif'
this.ctx.fillText(this.fpsHistory.reduce((a,b) => a+b) / 5, 50, 50)
window.requestAnimationFrame(this.update.bind(this))
}
}
new App()
<script src="https://cdnjs.cloudflare.com/ajax/libs/victor/1.1.0/victor.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chroma-js/2.1.0/chroma.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/simplex-noise/2.4.0/simplex-noise.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/tweakpane@1.3.2/dist/tweakpane.min.js"></script>
body {
overflow: hidden;
background-image: radial-gradient(
circle at center,
#E08E79,
#F1D4AF,
#ECE5CE,
#C5E0DC
);
& > .canvas_wrapper {
width: 100vw; height: 100vh;
-webkit-filter: url("#goo");
filter: url("#goo");
& > canvas {
width: 100%; height: 100%;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment