Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URLs added here will be added as <link>s in order, and before the CSS in the editor. You can use the CSS from another Pen by using its URL and the proper URL extension.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                
              
            
!

CSS

              
                body {
  margin: 0;
  overflow: hidden;
  background-color: #000;
}

canvas {
  width: 100vw;
  height: 100vh;
}

              
            
!

JS

              
                console.clear();

const size = { width: 800, height: 600 };
const ratio = size.width / size.height;

const app = new PIXI.Application({
  width: size.width,
  height: size.height,
  backgroundColor: 0x1f1f33,
  resolution: window.devicePixelRatio || 1
});

document.body.appendChild(app.view);
app.stage.hitArea = app.screen;
app.stage.interactive = true;
app.stage.on("click", onClick);

let fireReady = true;
// Click event

function onClick(event) {
  // launch if we are ready
  if (fireReady) {
    launchRocket(event.data.global.x, event.data.global.y);
  }
}

// Global utility functions
function fit(value, oldmin, oldmax, newmin, newmax) {
  let num = value - oldmin;
  return num * (newmax / (oldmax - oldmin)) + newmin;
}

function getRandomLightColor() {
  return (
    "0x" +
    Math.floor(Math.random() * 222 + 33).toString(16) +
    Math.floor(Math.random() * 222 + 33).toString(16) +
    Math.floor(Math.random() * 222 + 33).toString(16)
  );
}

// draw BG
const bgTexture = PIXI.Texture.from(
  "https://i.postimg.cc/y8Z4LTmv/skyline.png"
);
const bg = new PIXI.Sprite(bgTexture);
app.stage.addChild(bg);

// resize
onResize();

// Graphics for the rocket
const graphics = new PIXI.Graphics();
graphics.beginFill(0xffff99);
graphics.drawCircle(0, 0, 3);
graphics.endFill();

const particleTexture = app.renderer.generateTexture(
  graphics,
  PIXI.SCALE_MODES.LINEAR,
  window.devicePixelRatio
);
// It's now a Sprite so safe to dispose
graphics.destroy();

const width = window.innerWidth / 2;
const height = window.innerHeight / 2;

// Use GSAP's ticker instead
app.ticker.stop();

class Particle {
  constructor(color, x, y, dx, dy) {
    this.birth = Date.now();
    this.color = color;
    this.sprite = null;
    this.exploded = false;
    this.x = x;
    this.y = y;
    this.dx = dx;
    this.dy = dy;
    this.init();
  }

  init() {
    const graphics = this.createGraphics(this.color);
    const texture = this.createTexture(graphics);
    graphics.destroy();
    const sprite = this.createSprite(texture);
    this.sprite = sprite;
    return sprite;
  }

  createGraphics(color) {
    const graphics = new PIXI.Graphics();
    graphics.beginFill(color);
    graphics.drawCircle(0, 0, 3);
    graphics.endFill();

    return graphics;
  }

  createTexture(graphics) {
    const particleTexture = app.renderer.generateTexture(
      graphics,
      PIXI.SCALE_MODES.LINEAR,
      window.devicePixelRatio
    );

    return particleTexture;
  }

  createSprite(texture) {
    const sprite = new PIXI.Sprite(texture);
    sprite.anchor.set(0.5);
    sprite.x = this.x;
    sprite.y = this.y;
    sprite.scale.set(0.2, 1);
    const blurFilter = new PIXI.filters.BlurFilter();
    blurFilter.blur = 2;
    sprite.filters = [blurFilter];
    return sprite;
  }

  getSprite() {
    return this.sprite;
  }
}

// Create Particles
const particles = []; // sprite container
const particleContainer = new PIXI.ParticleContainer();

const trailEmitter = new PIXI.particles.Emitter(
  particleContainer,
  [PIXI.Texture.from("https://i.postimg.cc/hvKrzF9J/particle.png")],
  {
    alpha: {
      start: 0.8,
      end: 0.1
    },
    scale: {
      start: 0.1,
      end: 0.01,
      minimumScaleMultiplier: 0.5
    },
    color: {
      start: "#ec5204",
      end: "#ffff00"
    },
    speed: {
      start: 5,
      end: 500,
      minimumSpeedMultiplier: 1
    },
    acceleration: {
      x: 0,
      y: 500
    },
    maxSpeed: 0,
    startRotation: {
      min: 0,
      max: 360
    },
    noRotation: true,
    rotationSpeed: {
      min: 0.5,
      max: 1
    },
    lifetime: {
      min: 0.5,
      max: 1
    },
    blendMode: "screen",
    frequency: 0.007,
    emitterLifetime: -1,
    maxParticles: 1000,
    pos: {
      x: 0,
      y: 0
    },
    addAtBack: false,
    spawnType: "point"
  }
);

// CONSTANTS
const w = window.innerWidth;
const h = window.innerHeight;
const gravity = 0.5;
const damping = 0.99;

const dots = []; // the array of 3D fragments
const dotSprites = [];

// randomize the number of fragments
const dotsNum = fit(Math.random(), 0, 1, 80, 100); // the amount of fragments
const projCenterX = w / 2; // use horizontal center of canvas
const projCenterY = h / 2; // use vertical center of canvas
const perspScale = width * 0.8;

// The Rocket
function launchRocket(posX, posY) {
  fireReady = false;
  const dx = 0;
  const dy = fit(Math.random(), 0, 1, -3, -5);
  const color = getRandomLightColor();
  const particle = new Particle(color, posX, posY, dx, dy);
  const dot = particle.getSprite();
  particles.push(particle);
  app.stage.addChild(dot, particleContainer);
}

// The Exploded Fragments
class Dot {
  constructor(color, birth) {
    this.color = color;
    this.birth = birth;
    this.sprite = null;
    this.alpha = 1;

    // The initial extents of the explosion
    this.globeRadius = fit(Math.random(), 0, 1, 5, 10);
    // azimuth and altitude for laying out points
    this.theta = Math.random() * 2 * Math.PI; // cover the full sphere
    this.phi = Math.acos(Math.random() * 2 - 1); // half sphere

    // the x,y,z coordinates
    this.x = 0;
    this.y = 0;
    this.z = 0;

    // initial size of the dot
    this.radius = 1;

    // velocity vectors same speed in all directions
    this.dx = 0;
    this.dy = 0;
    this.dz = 0;

    // offset (mouselclick) position
    this.xPos = 0;
    this.yPos = 0;

    // the Screen coordinates
    this.xScreen = 0;
    this.yScreen = 0;
    this.scaleScreen = 0;
  }
  // Initialize particle positions
  init() {
    this.x = this.globeRadius * Math.sin(this.phi) * Math.cos(this.theta);
    this.y = this.globeRadius * Math.cos(this.phi);
    this.z =
      this.globeRadius * Math.sin(this.phi) * Math.sin(this.theta) +
      this.globeRadius;

    // the velocity vector can be the same as the point position since we expand radially
    this.dx = this.x;
    this.dy = this.y;
    this.dz = this.z;
  }

  // calculates the screen coordinates from 3D space
  project() {
    // Project the 3D coordinates onto the 2D plane
    this.scaleScreen = perspScale / (perspScale + this.z);
    this.xScreen = this.x * this.scaleScreen + projCenterX;
    this.yScreen = this.y * this.scaleScreen + projCenterY;
  }

  draw(xPos, yPos) {
    this.xPos = xPos;
    this.yPos = yPos;

    this.init();
    this.project();
    // Create Graphics element
    const graphics = new PIXI.Graphics();
    graphics.beginFill(this.color);
    graphics.drawCircle(0, 0, this.radius);
    graphics.endFill();

    // Turn it into a texture
    const particleTexture = app.renderer.generateTexture(
      graphics,
      PIXI.SCALE_MODES.LINEAR,
      window.devicePixelRatio
    );
    graphics.destroy();

    // turn it into a sprite
    const sprite = new PIXI.Sprite(particleTexture);
    sprite.anchor.set(0.5);
    sprite.x = this.xScreen + xPos - projCenterX;
    sprite.y = this.yScreen + yPos - projCenterY;
    // Apply some blur
    //const blurFilter = new PIXI.filters.BlurFilter();
    //blurFilter.blur = 1;
    //sprite.filters = [blurFilter];
    sprite.blendMode = PIXI.BLEND_MODES.SCREEN;

    this.sprite = sprite;

    return sprite;
  }

  update() {
    // add gravity to vertical dimension
    this.dy += gravity;

    // advance coordinates along their vectors
    const speed = fit(Math.random(), 0, 1, 0.1, 0.3);
    this.x += this.dx * speed;
    this.y += this.dy * speed;
    this.z += this.dz * speed;

    // add damping
    this.x *= damping;
    this.y *= damping;
    this.z *= damping;

    this.sprite.alpha -= 0.01;

    this.project();
    this.sprite.x = this.xScreen + this.xPos - projCenterX;
    this.sprite.y = this.yScreen + this.yPos - projCenterY;

    // remove particle if it is extinguished or older than 2 sec
    if (this.sprite.alpha <= 0 || Date.now() - this.birth > 2000) {
      spriteContainer.removeChild(this.sprite);
    }

    if (spriteContainer.children.length == 0) {
      fireReady = true;
    }
  }
}

// The containter for the fragments
const spriteContainer = new PIXI.ParticleContainer();
app.stage.addChild(spriteContainer);
// Keep adding to same container

function createExplosion(xPos, yPos, color, birthTime) {
  for (let i = 0; i < dotsNum; i++) {
    dots.push(new Dot(color, birthTime));
  }

  dots.map((dot, index) => {
    const sprite = dot.draw(xPos, yPos);
    dotSprites.push(sprite);
    spriteContainer.addChild(sprite);
  });

  //console.log(xPos, yPos, color);
}

// Use gsap's hearbeat
gsap.ticker.add((delta) => {
  app.ticker.update();

  const now = Date.now();

  // launch rockets until explode
  const dx = 1.5 * Math.sin(delta * 30);
  particles.map((part, index) => {
    const sprite = part.getSprite();
    sprite.x += dx;
    sprite.y += part.dy;

    const elapsed = now - part.birth;
    trailEmitter.updateOwnerPos(sprite.x, sprite.y);
    trailEmitter.update(elapsed * 0.001);

    if (elapsed > fit(Math.random(), 0, 1, 1000, 1500)) {
      particles.splice(index, 1);
      // store point of explosion
      const point = { explodeX: sprite.x, explodeY: sprite.y };
      //console.log(point);
      app.stage.removeChild(sprite, particleContainer);
      trailEmitter.cleanup();
      createExplosion(point.explodeX, point.explodeY, part.color, Date.now());
    }
  });
});

gsap.ticker.add((time, deltaTime, frame) => {
  dots.forEach((dot) => dot.update());
});

window.addEventListener("resize", onResize);

function onResize() {
  if (window.innerWidth / window.innerHeight >= ratio) {
    var w = window.innerHeight * ratio;
    var h = window.innerHeight;
  } else {
    var w = window.innerWidth;
    var h = window.innerWidth / ratio;
  }
  app.view.style.width = w + "px";
  app.view.style.height = h + "px";
}

              
            
!
999px

Console