<canvas id="c"></canvas>
* {
  margin: 0;
  padding: 0;
  overflow: hidden;
}

body {
  overflow: hidden;
}

canvas {
  display: block;
}
/* The following mimics the starter shim */
var a = document.getElementById( 'c' ),
	b = document.body,
	c = a.getContext( '2d' );

a.width = window.innerWidth;
a.height = window.innerHeight;

/*
Minified

function L(){for(i=0;i<K.length;i++)d[K[i]]=d[K[i]+1]=d[K[i]+2]=0;for(z=0>z?z+360:z-3,b=z*(P/180),r=s(b)*l+v>>0,g=s(b+2*(P/3))*l+v>>0,b=s(b+4*(P/3))*l+v>>0,i=0;i<C;i++){var n=S[i],a=m[0]-n[0],t=m[1]-n[1],e=M.atan2(t,a),o=100/M.sqrt(a*a+t*t);n[2]+=M.cos(e)*o,n[3]+=s(e)*o,n[0]+=n[2],n[1]+=n[3],n[2]*=.96,n[3]*=.96,n[0]=n[0]>=w?n[0]-w:n[0]<0?n[0]+w-1:n[0],n[1]=n[1]>=h?n[1]-h:n[1]<0?n[1]+h-1:n[1],e=(n[1]+.5>>0)*w*4+4*(n[0]+.5>>0),K[i]=e,d[e]=r,d[e+1]=g,d[e+2]=b}c.putImageData(f,0,0),u&&requestAnimationFrame(L)}function R(){w=a.width=W.innerWidth,h=a.height=W.innerHeight,c.fillRect(0,0,w,h),f=c.getImageData(0,0,w,h),d=f.data}var W=window,S=[],K=[],m=[0,0],C=3e4,u=!0,w,h,f,d,r,g,b,z=1,M=Math,P=M.PI,s=M.sin,v=128.5,l=127;for(W.onresize=R,W.onmousemove=function(i){m[0]=i.pageX,m[1]=i.pageY},W.onclick=function(){u=!u,u&&L()},R(),i=0;i<C;i++)S.push([M.random()*w,M.random()*h,0,0]);L();

*/

/*
Unminified
*/

//Declare variables
var W = window,
	C = 30000, //number of particles
	S = [], //the array that holds all of the particles
	K = [], //holds all the old array pointers for particle coordinates
	m = [0, 0], //mouse coordinates
	u = true, //playing or paused
	w, h, f, d, //width, height, imagedata, actual data array
	r, g, b, z=1, //red, green, blue, degrees of color rotation
	M = Math,
	P = M.PI,
	s = M.sin,
	v = 128.5, //center of color range
	l = 127; //maximum deviation

//Main loop
function L(){

	//Set all old particle pixels back to black (way more efficient than clearing the whole canvas)
	for (i=0;i<K.length;i++) d[K[i]]=d[K[i]+1]=d[K[i]+2]=0;

	//Rotate through 360 degrees. If less than 0, add 360 to wrap around
    z=z<0?z+360:z-35;
	
	//This is what does the color rotation. It's three sine waves out of phase from each other by 120 degrees.
	b=z*(P/180); //Degrees to radians

    r=s(b)*l+v>>0; //Red (bit shifting was used for the rounding)
	g=s(b+2*(P/3))*l+v>>0; //Green
	b=s(b+4*(P/3))*l+v>>0; //Blue

	//Loop through all of the particles
	for (i=0;i<C;i++) {

		var n=S[i], //reference to current particle
	    	x=m[0]-n[0], //distance between particle and mouse on the X axis
	    	y=m[1]-n[1], //distance between particle and mouse on the Y axis
			k=M.atan2(y,x), //angle in radians
	    	o=100/M.sqrt(x*x+y*y); //gravity calculation

        n[2]+=M.cos(k)*o; //Velocity change on X axis
        n[3]+=s(k)*o; //Velocity change on Y axis

        n[0]+=n[2]; //Add current velocity to particle position on X axis
        n[1]+=n[3]; //Add current velocity to particle position on Y axis

        n[2]*=0.96; //Reduce the X axis velocity (drag)
        n[3]*=0.96; //Reduce the Y axis velocity

        n[0]=n[0]>=w?n[0]-w:n[0]<0?n[0]+w-1:n[0]; //Wrap particle to opposite side of screen if it goes out of bounds on X axis
        n[1]=n[1]>=h?n[1]-h:n[1]<0?n[1]+h-1:n[1]; //Wrap particle to opposite side of screen if it goes out of bounds on Y axis

		k=(n[1]+0.5>>0)*w*4+(n[0]+0.5>>0)*4; //Get which point in the imagedata array is the current particle's new position
		
		K[i]=k; //Keep array position so it doesn't need to be calculated next loop (increases render speed a lot)

		d[k]=r; //Set the red channel
		d[k+1]=g; //Set the green channel
		d[k+2]=b; //Set the blue channel

	}

	c.putImageData(f, 0, 0); //Push the updated imagedata back to the canvas
	u && requestAnimationFrame(L) //If playing, loop again
}

//Window resize handler. stretches the canvas to fit screen and refreshes imagedata array.
function R() {
	w=a.width=W.innerWidth;
	h=a.height=W.innerHeight;
	c.fillRect(0,0,w,h);
	f=c.getImageData(0,0,w,h);
	d=f.data;
};

//Attach event handlers
W.onresize = R;
W.onmousemove = function(e) {m[0]=e.pageX; m[1]=e.pageY}; //Sets mouse coordinate values.
W.onclick = function() {u=!u; u&&L()}; //Toggle playing boolean and if playing, start loop.

R(); //Call the resize function once to size the canvas before we begin.

//This fills the particle array with randomly positioned particles.
for (i=0;i<C;i++) S.push([M.random()*w,M.random()*h,0,0]);

L(); //Start the loop.

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.