Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/vaneenige/phenomenon/llms.txt

Use this file to discover all available pages before exploring further.

Quickstart

This guide will walk you through creating your first working particle system with Phenomenon. You’ll learn how to set up the renderer, define particle attributes, write shaders, and render thousands of particles to the screen.

Prerequisites

Make sure you have:
  • Phenomenon installed (see installation guide)
  • A <canvas> element in your HTML
  • Basic understanding of JavaScript
No prior WebGL or shader experience is required - we’ll explain each step.

Create your first particle system

1

Import Phenomenon

First, import the library into your JavaScript file:
import Phenomenon from 'phenomenon';
2

Create the renderer

Initialize a Phenomenon instance. This sets up the WebGL context and rendering loop:
const phenomenon = new Phenomenon({
  settings: {
    devicePixelRatio: 1,
    position: { x: 0, y: 0, z: 3 },
  },
});
What’s happening here:
  • devicePixelRatio: 1 sets the rendering resolution (1 for better performance, window.devicePixelRatio for sharper quality)
  • position sets the camera’s position in 3D space (z: 3 moves the camera back so you can see the particles)
3

Define particle attributes

Attributes define the data for each particle. Each attribute needs a name (used in shaders), size (number of values), and a data function:
const attributes = [
  {
    name: 'aPositionStart',
    data: () => [
      Math.random() - 0.5,
      Math.random() - 0.5,
      Math.random() - 0.5,
    ],
    size: 3,
  },
  {
    name: 'aColor',
    data: () => [Math.random(), Math.random(), Math.random()],
    size: 3,
  },
];
What’s happening here:
  • aPositionStart gives each particle a random 3D position (x, y, z) centered around the origin
  • aColor assigns each particle a random RGB color
  • The data function is called once per particle to generate unique values
4

Write the vertex shader

The vertex shader positions each particle in 3D space:
const vertex = `
  attribute vec3 aPositionStart;
  attribute vec3 aPosition;
  attribute vec3 aColor;

  uniform mat4 uProjectionMatrix;
  uniform mat4 uModelMatrix;
  uniform mat4 uViewMatrix;

  varying vec3 vColor;

  void main(){
    gl_Position = uProjectionMatrix * uModelMatrix * uViewMatrix * vec4(aPositionStart + aPosition, 1.0);
    gl_PointSize = 2.0;
    vColor = aColor;
  }
`;
What’s happening here:
  • attribute variables receive per-particle data from our attributes array
  • uniform variables are the same for all particles (projection, model, view matrices handle camera positioning)
  • varying variables pass data to the fragment shader
  • gl_Position sets where the particle appears on screen
  • gl_PointSize controls how large each particle is (in pixels)
5

Write the fragment shader

The fragment shader colors each particle:
const fragment = `
  precision mediump float;

  varying vec3 vColor;

  void main(){
    gl_FragColor = vec4(vColor, 1.0);
  }
`;
What’s happening here:
  • varying vec3 vColor receives the color from the vertex shader
  • gl_FragColor sets the final pixel color (RGB + alpha)
6

Add the particle system

Now combine everything and add it to the renderer:
phenomenon.add('particles', {
  attributes,
  vertex,
  fragment,
  multiplier: 10000,
});
What’s happening here:
  • 'particles' is a unique identifier for this instance
  • multiplier: 10000 creates 10,000 particles (the data function runs 10,000 times)
  • Phenomenon automatically starts rendering

Complete example

Here’s the full code put together:
import Phenomenon from 'phenomenon';

// Create the renderer
const phenomenon = new Phenomenon({
  settings: {
    devicePixelRatio: 1,
    position: { x: 0, y: 0, z: 3 },
  },
});

// Define particle attributes
const attributes = [
  {
    name: 'aPositionStart',
    data: () => [
      Math.random() - 0.5,
      Math.random() - 0.5,
      Math.random() - 0.5,
    ],
    size: 3,
  },
  {
    name: 'aColor',
    data: () => [Math.random(), Math.random(), Math.random()],
    size: 3,
  },
];

// Vertex shader
const vertex = `
  attribute vec3 aPositionStart;
  attribute vec3 aPosition;
  attribute vec3 aColor;

  uniform mat4 uProjectionMatrix;
  uniform mat4 uModelMatrix;
  uniform mat4 uViewMatrix;

  varying vec3 vColor;

  void main(){
    gl_Position = uProjectionMatrix * uModelMatrix * uViewMatrix * vec4(aPositionStart + aPosition, 1.0);
    gl_PointSize = 2.0;
    vColor = aColor;
  }
`;

// Fragment shader
const fragment = `
  precision mediump float;

  varying vec3 vColor;

  void main(){
    gl_FragColor = vec4(vColor, 1.0);
  }
`;

// Add particle system to renderer
phenomenon.add('particles', {
  attributes,
  vertex,
  fragment,
  multiplier: 10000,
});
Open your browser’s developer console to check for WebGL errors if you don’t see any particles.

Adding animation

To make your particles move, add an onRender hook and use uniforms:
const uniforms = {
  uTime: {
    type: 'float',
    value: 0.0,
  },
};

phenomenon.add('particles', {
  attributes,
  vertex: `
    attribute vec3 aPositionStart;
    attribute vec3 aPosition;
    uniform float uTime;
    uniform mat4 uProjectionMatrix;
    uniform mat4 uModelMatrix;
    uniform mat4 uViewMatrix;

    void main(){
      vec3 pos = aPositionStart + aPosition;
      pos.y += sin(uTime + aPositionStart.x * 3.0) * 0.1;
      gl_Position = uProjectionMatrix * uModelMatrix * uViewMatrix * vec4(pos, 1.0);
      gl_PointSize = 2.0;
    }
  `,
  fragment,
  uniforms,
  multiplier: 10000,
  onRender: (instance) => {
    instance.uniforms.uTime.value += 0.01;
  },
});
Now your particles will wave up and down!

Next steps

You’ve created your first particle system! Here’s what to explore next:

API Reference

Learn about all available options and methods

Examples

Browse more complex particle effects and techniques

Shaders Guide

Deep dive into writing custom vertex and fragment shaders

Performance Tips

Optimize your particle systems for maximum FPS

Build docs developers (and LLMs) love