<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