Skip to content

Instantly share code, notes, and snippets.

@anon987654321
Created February 5, 2026 03:00
Show Gist options
  • Select an option

  • Save anon987654321/aa3b7506fdc9b461aa8504990a27f2eb to your computer and use it in GitHub Desktop.

Select an option

Save anon987654321/aa3b7506fdc9b461aa8504990a27f2eb to your computer and use it in GitHub Desktop.
The Chaos Orb
<canvas id="c"/>
var ctx, mid;
var dots = [], target = [];
var maxForce = 0.1247;
/* This code implements some steering behaviors to a set of 14000 particles.
Each particle will follow one target. There are 7 targets in this pen;
** WARNING: Lots of math and no 'real physics'. */
// A Vector Mini-library
function Vector(x, y) {
this.x = x;
this.y = y;
}
Vector.prototype.add = function (vector) {
this.x += vector.x;
this.y += vector.y;
}
Vector.prototype.sub = function (vector) {
this.x -= vector.x;
this.y -= vector.y;
}
Vector.prototype.mult = function (n) {
this.x *= n;
this.y *= n;
}
// Returns the "length" of a vector
Vector.prototype.norm = function () {
return (Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2)));
}
/* Moves a particle/target around. If it hits a wall
(in this case, a circular barrier with a 190px radius,
centered in the middle of the canvas), the particle
goes to the oposite direction, with a smaller velocity. */
Vector.prototype.move = function (delta) {
if(dist(this.x + delta.x, this.y + delta.y, mid.x, mid.y) > 190) {
delta.mult(-0.43);
}
this.add(delta);
}
// Dat Dot, a.k.a particle.
function Dot(x, y) {
this.pos = new Vector(x, y); // It has a position,
this.vel = new Vector(rand(), rand()); // A velocity,
this.vel.mult(5 / this.vel.norm());
this.acc = new Vector(rand(), rand()); // And an acceleration.
this.acc.mult(5 / this.acc.norm());
// The vectors are normalized, for the sake of AWESOME EXPLOSIONS :D
}
/* At each frame, each particle will calculate its new
velocity based on the acceleration, which will affect
its current position. */
Dot.prototype.update = function() {
this.pos.move(this.vel);
this.vel.add(this.acc);
//The acc reset is a kludge. I'm Sorry...
//In other words, a workaround to a non-accumulative acceleration.
this.acc.mult(0);
}
/* In a nutshell, the particle movement is given by its current state + desired state */
Dot.prototype.seek = function(target) {
/* The Desired vector (des) is the shortest way
between the particle and its target. */
var des = new Vector(0, 0);
des.add(target);
des.sub(this.pos);
/* If the particle gets too close to the target,
It'll be scared and will run away. */
var d = des.norm();
des.mult((d < 25) ? -d : d);
/* We live in a world with mass, thus with inertia.
To give a more natural movement, we'll
create a steering vector, which is the
difference between the desired and the
current velocity. */
var steer = new Vector(0, 0);
steer.add(des);
steer.sub(this.vel);
/* The steering vector can be really strong, so
I created a sort of limit for it. */
if(steer.norm() > maxForce) {
steer.mult(maxForce / steer.norm());
}
return steer;
}
/* The steering force will determinate the particle's acceleration,
which will affect the rest in update() . */
Dot.prototype.behave = function(target) {
var seek = this.seek(target);
this.acc.add(seek);
}
// Just a random function. ]-1, 1[
function rand(){
return ((Math.random() > 0.5) ? Math.random() : -Math.random());
}
// 2D Euclidian Distance
function dist(x1, y1, x2, y2){
return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
}
function init() {
var dot, i;
// Create all the particles
for(i = 0; i < 14000; i++) {
dot = new Dot(mid.x, mid.y);
dots.push(dot);
}
// And their targets.
for(i = 0; i < 7; i++) {
dot = new Vector(mid.x, mid.y);
target.push(dot);
}
}
// Some house cleaning before the cool stuff happens.
function setup() {
var canvas = document.querySelector('#c');
canvas.width = 450;
canvas.height = 450;
ctx = canvas.getContext('2d');
ctx.fillStyle = "rgb(100, 23, 255)";
mid = new Vector(canvas.width / 2, canvas.height / 2);
}
function draw() {
var dot, i, j;
// Clear the previous frame.
ctx.clearRect(0, 0, 2 * mid.x, 2 * mid.y);
ctx.beginPath();
// For each target:
for (i = 0; i < target.length; i++) {
// Take a set of particles that will follow it,
for(j = Math.floor(i * dots.length / target.length);
j < Math.floor((i + 1) * dots.length / target.length);
j++) {
// Do all the magic stuff,
dot = dots[j];
dot.behave(target[i]);
dot.update();
ctx.rect(dot.pos.x, dot.pos.y, 1, 1);
}
// Move the target randomly within the given borders,
target[i].move(new Vector(
Math.floor(50 * rand()),
Math.floor(50 * rand())
));
}
// Draw everyting on the canvas at once
ctx.fill();
// And repeat it till the sun goes out.
requestAnimationFrame(draw);
}
window.onload = function() {
setup();
init();
requestAnimationFrame(draw);
}
body {
margin: 0px;
background: radial-gradient(circle, #217, #000);
}
canvas {
display: block;
margin: 100px auto;
background: radial-gradient(
circle,
rgba(20, 30, 7, 0.95) 7%,
rgba(80, 3, 240, 0.8) 19%,
transparent 60%
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment