<div id="canvas"></div>
html, body
{
	width: 100%;
	height: 100%;
	overflow: hidden;
	margin: 0;
	padding: 0;
	user-select: none;
}

body
{
	background-color: lightgray;
}

#canvas
{
	position: absolute;
	top: 0;
	bottom: 0;
	left: 0;
	right: 0;
}
View Compiled
console.clear();

const colors = [0xF9B52C, 0x333333];

class Stage 
{	
	element:HTMLElement;
	app:any;
	stage:any;
	particleContainer:any;
	containers:any[] = []
	particles:any[] = [];
	textures:any[] = [];
	glass:any;
	shine:any;
	
	constructor(canvas:HTMLElement)
	{
		this.element = canvas;
		this.app = new PIXI.Application(this.element.offsetWidth, this.element.offsetHeight, { antialias: false, backgroundColor : 0xD3CFE5 });
		this.element.appendChild(this.app.view);
		
		this.stage = new PIXI.Container();		
		this.app.stage.addChild(this.stage);
		
		this.particleContainer = new PIXI.Container();
		this.stage.addChild(this.particleContainer);
		
		this.onResize();		
	}
	
	onResize = function()
	{
		this.app.renderer.resize(this.element.offsetWidth, this.element.offsetHeight);
		this.stage.position.x = window.innerWidth / 2;
		this.stage.position.y = window.innerHeight / 2;
	}
	
	newParticle = function(color)
	{
		let graphic = new PIXI.Graphics();

			graphic.lineStyle(0);
			graphic.beginFill(color, 0.8);
			graphic.drawCircle(0, 0, 2);
			graphic.endFill();

		let texture = this.app.renderer.generateTexture(graphic);
		
		let sprite = new PIXI.Sprite(texture);
		this.particles.push(sprite);
		this.particleContainer.addChild(sprite);
		
		return sprite;
	}

}

class Sim
{
	private world:any;
	private width:number = 0;
	private height:number = 0;
	private particleSystem:any;
	private particle:any;
	
	private timeStep:number = 1.0 / 60.0;
	private velocityIterations:number = 8;
	private positionIterations:number = 3;
	
	private cooldown:number = 200;
	private cooling:boolean = false;
	
	const METER:number = 100;
	const OFFSET_X:number = 0;
	const OFFSET_Y:number = 0;
	const PADDING:number = 50;

	constructor(world)
	{
		this.world = world;
		
		let liquidContainerDef = new b2BodyDef();
		let liquidContainer = this.world.CreateBody(liquidContainerDef);
		
		this.onResize();
		
		let floor = this.createWallShape(
			this.width / this.METER / 2,
			0.05,
			new b2Vec2(this.width / this.METER / 2, this.height / this.METER + 0.05)
		)
		
		let leftWall = this.createWallShape(
			0.05,
			(this.height + 1000) / this.METER,
			new b2Vec2(-0.05, 0)
		)
		
		let rightWall = this.createWallShape(
			0.05,
			(this.height + 1000) / this.METER,
			new b2Vec2(this.width / this.METER + 0.05, 0)
		)
		
		liquidContainer.CreateFixtureFromDef(floor);
		liquidContainer.CreateFixtureFromDef(leftWall);
		liquidContainer.CreateFixtureFromDef(rightWall);
		
		let particleSystemDef = new b2ParticleSystemDef();
			particleSystemDef.radius = 0.03;
			particleSystemDef.dampingStrength = 0.2;
		
		this.particleSystem = this.world.CreateParticleSystem(particleSystemDef);
		
		this.particle = new b2CircleShape();
	}

	onResize = function()
	{
		let h = window.innerHeight;
		this.width = 254;
		this.height = 180;
		this.height -= this.PADDING;	
	}
	
	private createWallShape(width, height, angle)
	{
		let wallShape = new b2PolygonShape();
		wallShape.SetAsBoxXYCenterAngle(width, height, angle, 0);
		
		let fixtureDef = new b2FixtureDef();
		fixtureDef.shape = wallShape;
		fixtureDef.density = 5;
		
		return fixtureDef;
	}
	
	getParticles():number[]
	{
		return this.world.particleSystems[0].GetPositionBuffer();
	}
	
	step = function()
	{
		this.world.Step(this.timeStep, this.velocityIterations, this.positionIterations);
    	this.time += 1 / 60;
	}
	
	addParticles = function()
	{		
		if(!this.cooling)
		{
			this.cooling = true;
			this.particle.position.Set((this.width / 2) / this.METER, -this.height * 2 / this.METER);
			this.particle.radius = 1;

			let particleGroupDef = new b2ParticleGroupDef();
				particleGroupDef.shape = this.particle;

			this.particleSystem.CreateParticleGroup(particleGroupDef);
			
			setTimeout(() => { this.cooling = false }, this.cooldown)
		}
	}
}

let stage = new Stage(document.getElementById('canvas'));

let gravity = new b2Vec2(0, 10);
let world = new b2World(gravity);
let sim = new Sim(world);

// window.addEventListener('resize', e => 
// { 
// 	sim.onResize();
// 	stage.onResize();
// });

let simOutput: any[] = [];
let runFor:number = 10;

function runSim()
{
	for(let i = 0; i < runFor * 60; i++)
	{
		sim.step();
		var particles = sim.getParticles();
		
		let simTick = [];
		

		for (var j = 0; j < particles.length / 2; j++)
		{			
			var x = (sim.width/2) - particles[j * 2] * sim.METER + sim.OFFSET_X;
			var y = (sim.height/2) - (sim.height - particles[(j * 2) + 1] * sim.METER + sim.OFFSET_Y);
			simTick.push({x: x, y: y});
		}	
		
		simOutput.push(simTick);
	}
}

let init = function()
{
	sim.addParticles();
	runSim();

	let frame = 0;
	
	var particles = simOutput[simOutput.length - 1];
	
	let xMin;
	let xMax;
	let yMin;
	let yMax;
	
	// get bounds
	
	for (var i = 0; i < particles.length; i++)
	{
		if(!xMin || particles[i].x < xMin) xMin = particles[i].x;
		if(!xMax || particles[i].x > xMax) xMax = particles[i].x;
		if(!yMin || particles[i].y < yMin) yMin = particles[i].y;
		if(!yMax || particles[i].y > yMax) yMax = particles[i].y;
	}
	
	let areaWidth = xMax - xMin;
	let areaHeight = yMax - yMin;
	
	
	for (var i = 0; i < particles.length; i++)
	{
		let x = ((particles[i].x - xMin) / areaWidth) * 100;
		let y = ((particles[i].y - yMin) / areaHeight) * 100;
		//console.log(((particles[i].x + (sim.width/2)) / sim.width) * 100, Math.floor(particles[i].y))
		stage.newParticle(getPixel(imageData, x, y));
	}

	function playSim() 
	{
		//sim.step();

		var particles = simOutput[frame];
		for (var i = 0; i < particles.length; i++)
		{
			let p = !stage.particles[i] ? stage.newParticle(0x666666) : stage.particles[i];
			p.position.set(particles[i].x, particles[i].y);	
		}

		frame++;
		if(frame < simOutput.length) requestAnimationFrame(playSim);
		else requestAnimationFrame(realtimeTick);
	}

	playSim();
}

function realtimeTick() 
{
    sim.step();

    var particles = sim.getParticles();
    for (var i = 0; i < particles.length / 2; i++)
    {
		let p = !stage.particles[i] ? stage.newParticle() : stage.particles[i];
		
		if(p.position.y > window.innerHeight/2 && !p.removed)
		{
			stage.remove(p, p.index);
			p.removed = true;
		}
		else
		{
			var x = (sim.width/2) - particles[i * 2] * sim.METER + sim.OFFSET_X;
			var y = (sim.height/2) - (sim.height - particles[(i * 2) + 1] * sim.METER + sim.OFFSET_Y);
			p.position.set(x, y);
		}
    }

    requestAnimationFrame(realtimeTick);
}

//     requestAnimationFrame(tick);
// }

// window.addEventListener('click', () => { sim.addParticles(); })
// window.addEventListener('touchstart', () => { sim.addParticles(); })

// if(location.pathname.match(/fullcpgrid/i))
// {
// 	document.getElementById('info').style.visibility = "hidden";
// 	setInterval(() => {sim.addParticles()}, 500);
// }

// tick();

// var img = new Image();
let imageData;

// img.onload = function() 
// {
// 	imageData = getImageData(img);
//     init();
// };
// img.src = ;

const loader = new PIXI.loaders.Loader();
loader.add('hello', 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/557388/HELLO.png')
loader.load((loader, resources) => {
   // resources.bunny
   // resources.spaceship
	console.log(resources.hello);
	imageData = getImageData(resources.hello.data);
	//console.log(getPixel(imageData, 0, 0));
	init();
});

function getImageData( image ) {

    var canvas = document.createElement( 'canvas' );
    canvas.width = image.width;
    canvas.height = image.height;

    var context = canvas.getContext( '2d' );
    context.drawImage( image, 0, 0 );

    return context.getImageData( 0, 0, image.width, image.height );

}

function getPixel(imgData, percentX, percentY) 
{
	let x = Math.round((percentX / 100) * imageData.width);
	let y = Math.round((percentY / 100) * imageData.height);
	
	if(x < 0) x = 0;
	if(x > imageData.width) x = imageData.width;
	if(y < 0) y = 0;
	if(y > imageData.height) y = imageData.height;
	
	//console.log('x', x, 'y', y, 'width', imageData.width, 'height', imageData.height)
	var r, g, b, a, offset = x * 4 + y * 4 * imgData.width;
	if(offset + 3 > imgData.data.length) offset = imgData.data.length - 4;
	//if(percentX > 99) console.log(x)
	//if(percentY > 98 && percentX > 98) console.log(x, y)
	r = imgData.data[offset];
	g = imgData.data[offset + 1];
	b = imgData.data[offset + 2];
	
	let str = "0x" + componentToHex(r) + componentToHex(g) + componentToHex(b);
    //console.log(str);
	return parseInt(str);


}

function componentToHex(c) {
    var hex = c.toString(16);
    return hex.length == 1 ? "0" + hex : hex;
}
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. //codepen.io/steveg3003/pen/zBVakw.js
  2. https://cdnjs.cloudflare.com/ajax/libs/pixi.js/4.4.3/pixi.min.js
  3. https://google.github.io/liquidfun/testbed/liquidfun.js