.container
  .reveal
    img
  canvas#particles
button.btn-trigger#trigger poof!
View Compiled
body{
  background: #333;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  height: 100vh;
}
.container{
  position: relative;
  width: 800px;
  height: 600px;
  background: #222 url(https://c.tadst.com/gfx/750x500/smoke-and-mirrors-day.jpg?1) no-repeat center center;
  background-size: cover;
}
.reveal{
  position: absolute;
  top: 400px;
  left: 390px;
  img{
    position: absolute;
    bottom: 0;
    transform: translate(-50%,0);
    max-width: 200px;
  }
}
.smoke-particle{
  background: url(https://mephysto.github.io/sandbox/smoke.png) no-repeat center center;
  background-size: cover;
  width: 100px;
  height: 86px;
}
canvas{
  position: absolute;
  top: 0;
  left: 0;
}
button{  
    border: none;
    background: white;
    font-size: 3em;
    text-transform: uppercase;
    padding: 10px 40px;
    margin-top: 45px;
    border-radius: 50px;
    line-height: 1em;
    box-shadow: 5px 5px 0 0 #000;
}
View Compiled
var canvas, ctx;
const particleCount = 200;
let particles = [];
const cWidth = 800;
const cHeight = 600;
const maxLife = 8000;
const minLife = 5000;

// const imagePath = "http://www.freeiconspng.com/uploads/smoke-12.png";
// const imagePath = "http://gallery.yopriceville.com/var/resizes/Free-Clipart-Pictures/Smoke-PNG/Smoke_Transparent_PNG_Clipart_Image.png";
// const imagePath = "https://mephysto.github.io/sandbox/smoke.png";
// const imagePath = "http://www.pngmart.com/files/3/Smoke-Transparent-PNG.png";
const imagePath = "http://www.fun-lover.com/graphic-shop/Halloween/images/FireSmoke/smoke-036.png";

var stage;
var canvas;
var bitmap;
var image = new Image();

function rand(min, max) {
  return (Math.floor(Math.random() * (max*1000 - min*1000 + 1)) + min*1000)/1000;
}

// one particle class
function smokeParticle(){
  this.life = rand(minLife, maxLife);
  // TODO: add origin point randomizer
  this.asset = this.asset || new createjs.Bitmap(image);
  this.origassetWidth =  this.asset.image.width;
  this.origassetHeight =  this.asset.image.height;
  this.assetWidth =  this.asset.image.width;
  this.assetHeight =  this.asset.image.height;
  this.originX = canvas.width * .5 - (this.assetWidth * 0.5);
  this.originY = canvas.height * .5 - (this.assetHeight * 0.5);
  this.asset.x = this.originX;
  this.asset.y = this.originY;
  this.reset = function(){
    this.assetWidth =  this.asset.image.width;
    this.assetHeight =  this.asset.image.height;
    this.asset.scaleX = rand(0.2, .5);
    this.asset.scaleY = rand(0.2, .5);
    
    this.originX = canvas.width * .5 - (this.origassetWidth * .5 * this.asset.scaleX);
    this.originY = canvas.height * .5 - (this.origassetHeight * .5 * this.asset.scaleY);
    
    this.asset.alpha = rand(0.5, 1);
    this.valpha = rand(0.02, 0.03);
    this.vx = (Math.random() - 0.5) * 20;
    this.vy = (Math.random() - 0.5) * 15;
    this.rotation = (Math.random() - 0.5) * .5;
    this.life =  rand(minLife, maxLife);
    this.asset.alpha = rand(0.5, 1);
    this.asset.x = this.originX;
    this.asset.y = this.originY;
  }
}
// (re)set all particle values to start
const trigger = function(){
  particles.map(function(particle){
     particle.reset();
     stage.addChild(particle.asset);
  });
}
// initial set up for all particles
const build = function(){
  // create all particles
  for (var i = particleCount - 1; i >= 0; i--) {
    let newParticle = new smokeParticle();
    particles.push(newParticle);
  }
  // put all particles on stage
  trigger();
  createjs.Ticker.setFPS(90);
  createjs.Ticker.addEventListener("tick", render);
}
// what happens during a render
const render = function(){
  let part;
  for (var i = particles.length - 1; i >= 0; i--) {
    part = particles[i];
    part.asset.x = part.asset.x + part.vx;
    part.asset.y = part.asset.y + part.vy;
    part.asset.rotation = part.asset.rotation + part.rotation;
    if(part.asset.x > canvas.width || part.x < 0){
      stage.removeChild(part.asset);
    }
    if(part.asset.y > canvas.height || part.y < 0){
      stage.removeChild(part.asset);
    }
    part.life--;
    if (part.life <= 0){
      stage.removeChild(part.asset);
    }
    // part.asset.alpha-=0.042;}
    part.asset.alpha = part.asset.alpha - part.valpha;
    
  }
  stage.update();
}

const PreloadAssets = (_options) => {
  let imgcount = 0,
    callback,
    img
  var preloadImage = (assetURL, assetsArray) => {
    if (assetURL === '') {
      imgcount++
      if (imgcount === assetsArray.length) {
        callback.call()
      }
    } else {
      img = new Image()
      img.onload = () => {
        imgcount++
        if (imgcount === assetsArray.length) {
          callback.call()
        }
      }
      img.src = assetURL
    }
  }

  return {
    preload: (_array, _callback) => {
      callback = _callback
      for (var i = 0; i < _array.length; i++) {
        preloadImage(_array[i], _array)
      }
    }
  }
}


// on DOM init
function init() {
  let onImageLoaded = function (e){
    console.log(`let's start`);
    build();
  }
  canvas = document.getElementById('particles');
  stage = new createjs.Stage(canvas);
  canvas.width = cWidth;
  canvas.height = cHeight;
  image = new Image();
  image.name = "smoke";
  image.src = imagePath;
  image.onload = onImageLoaded;
  document.getElementById('trigger').onclick = onTriggerClick;
}

document.addEventListener("DOMContentLoaded", function() {
  init();
});

const randomImages = [
  'https://media.giphy.com/media/Hv4HcD1xAho1a/giphy.gif',
  'http://vignette2.wikia.nocookie.net/hayday/images/4/44/White_Bunny.png/revision/latest/scale-to-width-down/100?cb=20150711214507',
  'https://lh3.googleusercontent.com/kMP05B4UUGphEbLz0Xo-1pPgOfLG2Ezowu-e1YFEyNSh43QD_6gFNgi3IyWbeH4y7U4=w100',
  'https://i.gifer.com/tGW.gif',
  // 'http://findicons.com/files/icons/1012/racing_cars/128/ford_gt.png',
  // 'https://66.media.tumblr.com/avatar_7fcda8ab63b1_128.png',
  'http://www.yes24.vn/Upload02/Event/201601/PPAP_animation.gif',
  'http://www.yes24.vn/Upload02/Event/201601/PPAP_animation.gif',
  'http://78.media.tumblr.com/tumblr_mbnik2lM8i1qdpzt2o1_500.gif',
  'https://66.media.tumblr.com/b2bfb7a9e3a5aa156411a0ecae55c581/tumblr_o26u8a7Ym81v0w4w9o1_250.gif',
  'https://66.media.tumblr.com/1db6b33020ba33a5ec53a6d3c09ccff7/tumblr_nabnz3kUKx1rrckl5o1_500.gif'
]

PreloadAssets().preload(randomImages, () =>{});

const onTriggerClick = function(){
  let img = document.querySelector('.reveal img');
  let randimg = randomImages[~~(rand(0, randomImages.length))];
  while (img.src === randimg){
    randimg = randomImages[~~(rand(0, randomImages.length))];
  }
  console.log(randimg);
  img.src = randimg;
  trigger();
}
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/EaselJS/0.8.0/easeljs.min.js