Projects bibleweb Docs atmosphere-libraries-frameworks.md

2D Graphics Libraries for Web-Based Atmospheric Pixel Art Scenes

Last modified March 28, 2026

2D Graphics Libraries for Web-Based Atmospheric Pixel Art Scenes

Research date: March 2026 Purpose: Evaluate graphics libraries for BibleWeb atmospheric backgrounds — animated pixel art scenes with particles, water, campfire, floating dust, sky gradients, and lighting effects.


Context and Requirements

The BibleWeb app needs immersive, atmospheric backgrounds for Bible passages. These are decorative scene layers beneath the reading UI, not interactive games. Requirements:

  • Pixel art style with atmospheric effects: campfire, floating dust particles, animated water, sky gradients, light bloom
  • Must not impede initial page load — this is a web-first SaaS app
  • SvelteKit (Svelte 5 runes) + Vite build pipeline
  • SSR-safe: canvas must initialize client-side only
  • Smooth 60 FPS animation loop
  • Minimal bundle weight — prefer lazy-loading the graphics layer
  • Long-term maintainability (active community, not abandoned)

Bundle Size Reference Table

These are approximate min+gzip figures based on Bundlephobia and official sources. Full-library import sizes; tree-shaking potential is noted separately.

Library Min size Min+gzip Tree-shakeable Notes
PixiJS v8 ~1.1 MB ~280 KB Yes (v8+) Much smaller with selective imports
Three.js r170 ~1.6 MB ~490 KB Partial ~340 KB savings possible via tree-shaking
p5.js v1.11 ~800 KB ~270 KB No Monolithic; no tree-shaking
Phaser 3.90 ~3.5 MB ~980 KB Custom build Custom build can reach ~110 KB gzipped
Konva.js ~430 KB ~130 KB Partial Lightweight; no WebGL
Fabric.js ~700 KB ~220 KB No Heavier, DOM-oriented
Two.js ~150 KB ~50 KB Partial Very light; limited particle support
GSAP v3 ~67 KB ~23 KB Yes Animation only; needs a renderer
Custom Canvas 0 KB 0 KB N/A Zero cost; full control

Note: For a background scene that is lazy-loaded, even a 280 KB gzip payload is acceptable when deferred. The critical path budget should stay near zero.


Library Evaluations

1. PixiJS v8 — Top Recommendation

Version: 8.17.x (as of early 2026, actively released every few weeks) GitHub stars: ~44,000+ npm weekly downloads: ~400,000+

PixiJS is purpose-built for 2D rendering with WebGL/WebGPU and falls back to Canvas. It is the most mature, most used, and best-suited library for this exact use case.

Key features relevant to BibleWeb:

  • ParticleContainer renders 1,000,000 particles at 60 FPS on modern hardware (v8 is 3x faster than v7)
  • WebGL/WebGPU renderer with automatic Canvas 2D fallback
  • Sprite sheets, animated sprites, texture atlases — ideal for pixel art spritesheets
  • Filters API: blur, color matrix, displacement (water ripple simulation), bloom
  • Tiling sprites for scrolling sky/water backgrounds
  • Full tree-shaking in v8 via ES module imports — import only what you use
  • @pixi/react and svelte-pixi community wrappers available

SvelteKit compatibility: Excellent. Initialize inside onMount() to avoid SSR issues. The svelte-pixi package provides declarative component wrappers. A sample SvelteKit + PixiJS repo exists at github.com/mszu/svelte-kit-pixi-sample.

Bundle strategy: Lazy-load the entire graphics layer after page hydration:

<script>
  import { onMount } from 'svelte';

  onMount(async () => {
    const { Application, ParticleContainer, Sprite, Texture } = await import('pixi.js');

    const app = new Application();
    await app.init({
      canvas: document.getElementById('bg-canvas'),
      width: window.innerWidth,
      height: window.innerHeight,
      backgroundAlpha: 0,  // transparent — scene sits behind UI
      antialias: false,    // pixel art: disable AA
      resolution: window.devicePixelRatio,
    });

    // Particle dust
    const particles = new ParticleContainer(500, {
      position: true,
      alpha: true,
      scale: true,
    });
    app.stage.addChild(particles);

    // Add sprites...
    app.ticker.add((delta) => {
      // Update particle positions each frame
    });
  });
</script>

<canvas id="bg-canvas" class="absolute inset-0 pointer-events-none" />

Pros:

  • Best-in-class 2D WebGL performance
  • Particle system purpose-built for volume effects
  • Active development (weekly releases in 2025-2026)
  • Sprite/spritesheet support is first-class — pixel art is a natural fit
  • Displacement filter = animated water at near-zero cost
  • Tree-shakeable v8 bundle

Cons:

  • Still meaningful bundle weight even with tree-shaking (~150-280 KB gzipped depending on features used)
  • WebGL context limit (browsers allow ~8-16 simultaneous GL contexts per page)
  • Slightly complex API for non-game developers
  • svelte-pixi wrapper lags behind PixiJS releases

Verdict: Strong recommendation. PixiJS is the professional choice for this feature set.


2. Three.js (OrthographicCamera for 2D)

Version: r170+ (releases monthly) GitHub stars: ~105,000+

Three.js is the dominant 3D WebGL library but can be used for 2D via OrthographicCamera. The camera projects the scene with no perspective distortion, giving pixel-accurate 2D rendering.

Relevant for our use case:

  • PlaneGeometry + MeshBasicMaterial for sprites/quads
  • Custom shaders (GLSL) for campfire glow, water ripple, god rays
  • Points + BufferGeometry for custom particle systems
  • Post-processing passes: bloom, depth of field (via three/addons/postprocessing)
  • Can render pixel art with NearestFilter texture filtering

Bundle size with tree-shaking: The full Three.js package is ~490 KB gzipped, but tree-shaking can reduce this to ~150-200 KB gzipped for a minimal 2D scene. A basic OrthographicCamera + mesh + texture setup is relatively small. However, adding post-processing (bloom) brings in significant extra weight.

SvelteKit compatibility: Works via onMount(). No official Svelte wrapper, but straightforward to use with plain JS. The @threlte/core package provides Svelte 5 bindings but is primarily 3D-oriented.

Pros:

  • Maximum shader flexibility — GLSL gives full GPU power
  • Post-processing pipeline: bloom, blur, color grading
  • Massive ecosystem and learning resources
  • Arguably better for complex lighting effects (dynamic point lights, glow)

Cons:

  • Significant overkill for simple atmospheric backgrounds
  • 2D usage via OrthographicCamera is non-idiomatic; documentation is 3D-centric
  • Tree-shaking is partial — the module structure is improving but not as clean as PixiJS v8
  • No built-in sprite/spritesheet/pixel art tooling
  • Steeper learning curve for a non-3D use case

Verdict: Only worth it if you plan to add GLSL shaders for advanced effects (dynamic lighting, god rays). For particle-based atmospheric scenes without custom shaders, Three.js is overkill. Revisit if the scope expands.


3. p5.js — Creative Coding Library

Version: 2.x (major rewrite launched 2025) GitHub stars: ~22,000+

p5.js is a creative coding library inspired by Processing, designed for artists and educators. Its draw() loop pattern makes procedural animation natural and readable.

Relevant features:

  • draw() loop called each frame — dead simple animation model
  • noise() function (Perlin noise) for organic particle movement and cloud generation
  • createVector() for particle physics
  • WEBGL mode for GPU-accelerated rendering
  • loadImage() + image() for sprite rendering (but no spritesheet support)
  • blendMode() for additive blending (fire, glow)

Bundle size: ~800 KB minified, ~270 KB gzipped. Monolithic — no tree-shaking. p5.js 2.x introduced a modular architecture to address this, but the ecosystem is still stabilizing.

SvelteKit compatibility: Works in onMount(). The p5 instance mode avoids global pollution. No official Svelte 5 bindings.

<script>
  import { onMount } from 'svelte';

  onMount(async () => {
    const p5 = (await import('p5')).default;

    new p5((sketch) => {
      const particles = [];

      sketch.setup = () => {
        sketch.createCanvas(window.innerWidth, window.innerHeight);
        sketch.noStroke();
      };

      sketch.draw = () => {
        sketch.clear();
        particles.forEach(p => {
          p.y -= 0.5 + sketch.noise(p.x * 0.01, sketch.frameCount * 0.01);
          sketch.fill(255, 200, 100, p.alpha);
          sketch.ellipse(p.x, p.y, p.size);
        });
      };
    }, document.getElementById('bg-canvas'));
  });
</script>

Pros:

  • Perlin noise built-in — excellent for organic atmospheric effects
  • Extremely readable, beginner-friendly API
  • p5.js 2.x modular architecture may reduce bundle size significantly in future
  • Great for procedural generation (clouds, terrain, dust)

Cons:

  • Monolithic bundle — ~270 KB gzipped even if you use 10% of the API
  • Not optimized for WebGL particle volume (no GPU batching equivalent)
  • Slower than PixiJS for large numbers of particles
  • No spritesheet/animation frame support natively
  • Canvas 2D mode is CPU-bound; hits limits at ~500-1000 complex particles

Verdict: Good for prototyping or procedural generative art. Not recommended for production if performance or bundle size is a constraint. Consider it for a pure-CSS/Canvas fallback implementation.


4. Phaser 3 — Game Framework

Version: 3.90 "Tsugumi" GitHub stars: ~37,000+

Phaser is a full-featured HTML5 game framework. It includes physics, scene management, input, audio, tweens, tilemap support, and more.

Bundle size: Default build is ~980 KB gzipped — the heaviest option. However, the phaserjs/custom-build tool allows selective compilation. A minimal custom build (renderer only + particles) can reach approximately 110 KB gzipped.

Why it may be overkill: Phaser's game loop, scene manager, input system, physics engine, and audio manager are all unnecessary for a decorative background effect. You'd be loading a full game engine for 5% of its capabilities.

Where it could shine: If BibleWeb ever adds interactive elements to backgrounds (clickable objects, mini-games, Bible story animations), Phaser's scene graph and tween system would become valuable.

SvelteKit compatibility: Works via dynamic import in onMount(). No official Svelte bindings. Phaser manages its own canvas lifecycle.

Pros:

  • Mature particle system with emitters, zones, gravity, lifespan
  • Built-in tween system for animations
  • Excellent documentation and large community
  • Custom build can reduce size significantly

Cons:

  • Default bundle is enormous for a background effect
  • Custom build requires build tooling overhead
  • Game-centric API is conceptually heavy for UI-adjacent work
  • Unnecessary features (physics, audio, input) ship even in minimal builds

Verdict: Not recommended for decorative backgrounds. The bundle cost and conceptual overhead are not justified unless BibleWeb expands into interactive animated content.


5. Konva.js and Fabric.js — Canvas Shape Libraries

These libraries serve a different primary purpose: interactive canvas drawing with selectable, draggable shapes — think diagram editors and design tools.

Konva.js:

  • ~130 KB gzipped
  • Layer-based Canvas 2D rendering
  • Built-in tweening and animation
  • svelte-konva package available with SvelteKit support
  • Dirty region detection for efficient re-renders

Fabric.js:

  • ~220 KB gzipped
  • SVG-to-canvas rendering pipeline
  • Rich object model (groups, text, paths)
  • Heavier than Konva; more DOM-oriented

For atmospheric scenes: Neither library is designed for high-performance frame-based animation or GPU-accelerated particle systems. They optimize for interactive object manipulation, not 60 FPS scene rendering with thousands of particles. Konva can handle moderate animation (campfire sprite cycling, slow-moving particles <100), but will struggle with volume effects.

Verdict: Not recommended for this use case. Useful only if the scene needs to be highly interactive (clickable objects, drag-and-drop). Konva is a reasonable fallback for very simple scenes (a few animated sprites, no particles).


6. Two.js — Renderer-Agnostic 2D API

Version: 0.8.x GitHub stars: ~8,000+

Two.js provides a unified 2D drawing API that can render to SVG, Canvas 2D, or WebGL. The same scene graph renders across all three backends.

Bundle size: ~50 KB gzipped — the lightest dedicated rendering library. If only using the SVG renderer, you can exclude other renderers and reduce size further.

Relevant features:

  • Shapes, paths, groups — good for vector-style backgrounds
  • Animation loop via requestAnimationFrame
  • WebGL renderer for GPU-backed rendering
  • ES6 module imports (partial tree-shaking)

Limitations for pixel art: Two.js is vector-geometry-oriented. It has no native sprite/texture/image system suited to pixel art animation frames or spritesheet playback. Particle systems would need to be hand-built.

Community health: Moderate — slower release cadence than PixiJS. The library is maintained but not as actively developed. Last major release was several years ago, though the project is not abandoned.

Verdict: Interesting for SVG-style atmospheric art (vector clouds, geometric particle trails), but not a fit for pixel art spritesheet animation. Too limited for the full requirements.


7. Custom Canvas/WebGL — Zero Dependencies

Building a custom renderer means writing directly against the Canvas 2D API or WebGL, with no library overhead.

When custom beats libraries:

  • Scene is simple: a sky gradient + one animated sprite + 50 dust particles
  • Bundle size is the primary constraint
  • The team has Canvas 2D API experience
  • You want pixel-perfect control over rendering order and blend modes

Canvas 2D approach (simple scenes):

<script>
  import { onMount } from 'svelte';

  onMount(() => {
    const canvas = document.getElementById('bg-canvas');
    const ctx = canvas.getContext('2d');
    const particles = Array.from({ length: 80 }, () => ({
      x: Math.random() * canvas.width,
      y: Math.random() * canvas.height,
      speed: 0.2 + Math.random() * 0.5,
      alpha: 0.1 + Math.random() * 0.4,
      size: 1 + Math.random() * 2,
    }));

    function drawGradient() {
      const grad = ctx.createLinearGradient(0, 0, 0, canvas.height);
      grad.addColorStop(0, '#0a0a1a');
      grad.addColorStop(1, '#1a2840');
      ctx.fillStyle = grad;
      ctx.fillRect(0, 0, canvas.width, canvas.height);
    }

    function loop() {
      drawGradient();
      particles.forEach(p => {
        p.y -= p.speed;
        if (p.y < 0) p.y = canvas.height;
        ctx.globalAlpha = p.alpha;
        ctx.fillStyle = '#fff8e0';
        ctx.fillRect(Math.floor(p.x), Math.floor(p.y), p.size, p.size);
      });
      ctx.globalAlpha = 1;
      requestAnimationFrame(loop);
    }

    loop();
  });
</script>

<canvas id="bg-canvas" class="absolute inset-0 pointer-events-none" />

When custom hits limits:

  • More than ~500 complex particles with per-particle alpha
  • Animated spritesheet playback (possible but manual)
  • Water displacement effects (Canvas 2D has no displacement filter)
  • Glow/bloom effects require pixel manipulation or an offscreen canvas

Pros:

  • Zero bundle cost
  • No third-party dependency to maintain or update
  • Full control over every pixel and draw call
  • No SSR issues — canvas is just a DOM element

Cons:

  • Must implement sprite animation, asset loading, particle physics by hand
  • Canvas 2D CPU-bound: ~200-500 particles before FPS drops on mid-range devices
  • No displacement filters (water ripple requires manual implementation)
  • Bloom/glow requires expensive ctx.shadowBlur or multi-pass compositing

Verdict: Start here for MVP. If you need more than ~500 particles, displacement maps, or GPU-backed effects, migrate to PixiJS.


8. GSAP (GreenSock Animation Platform)

Version: 3.x (now fully free including all plugins) GitHub stars: ~20,000+

GSAP is a JavaScript animation library, not a renderer. It animates CSS properties, SVG attributes, arbitrary JS objects, and Canvas context values. It does not draw to canvas itself.

Role in an atmospheric scene: GSAP complements a canvas renderer for higher-level transitions:

  • Fade in/out scene when switching Bible passages
  • Ease campfire brightness over time
  • Animate scene-level properties (daylight progression, fog density)
  • Orchestrate sequenced animations (sunrise → day → dusk cycle)
import gsap from 'gsap';

// Animate a scene transition
gsap.to(sceneConfig, {
  fogDensity: 0.8,
  ambientLight: 0.2,
  duration: 3,
  ease: 'power2.inOut',
  onUpdate: () => renderScene(sceneConfig),
});

Bundle size: ~23 KB gzipped — negligible. All plugins (SplitText, MorphSVG, ScrollTrigger) are now free.

SvelteKit compatibility: Excellent — pure JS, no DOM assumptions beyond the window global.

Verdict: Strong addition as a companion library to PixiJS or custom Canvas. Do not use it as the sole graphics system. Excellent for scene transitions, passage-linked atmosphere changes, and property interpolation.


SvelteKit-Specific Patterns

All canvas/WebGL libraries require client-side initialization. In SvelteKit, the standard pattern is:

<script>
  import { onMount, onDestroy } from 'svelte';
  import { browser } from '$app/environment';

  let canvas;
  let app; // PixiJS Application or custom renderer

  onMount(async () => {
    // Lazy import — not included in SSR bundle
    const { Application } = await import('pixi.js');

    app = new Application();
    await app.init({ canvas, backgroundAlpha: 0 });

    // Start scene...
  });

  onDestroy(() => {
    app?.destroy(false, { children: true, texture: true });
  });
</script>

<!-- canvas element must exist in DOM before onMount runs -->
<canvas bind:this={canvas} class="absolute inset-0 -z-10 pointer-events-none" />

Key rules:

  1. Always use onMount — never initialize canvas at module scope (breaks SSR)
  2. Dynamic import() inside onMount keeps the graphics library out of the SSR bundle entirely
  3. Use onDestroy to clean up the renderer and free GPU memory
  4. Use bind:this to get a reference to the canvas element
  5. Set pointer-events-none on the canvas so it does not intercept Bible text interactions
  6. Respect prefers-reduced-motion: skip animation or use a static image fallback

Comparison Summary

Criteria PixiJS v8 Three.js p5.js Custom Canvas
Particle performance Excellent Good Moderate Moderate
Pixel art spritesheets First-class Manual Basic Manual
Water displacement Built-in filter Shader only None Manual
Fire/glow effects Filters API Post-process blendMode() shadowBlur
Bundle (gzip) ~280 KB ~490 KB ~270 KB 0 KB
Tree-shakeable Yes (v8) Partial No N/A
SvelteKit compatibility Excellent Good Good Native
SSR safety onMount only onMount only onMount only onMount only
Community health Very active Very active Active N/A
Learning curve Moderate High Low Low-Medium
Maintenance burden Low (stable) Low (stable) Low (stable) High (custom)

Recommendation

Phase 1 — MVP (Ship fast)

Use custom Canvas 2D. Zero bundle cost, no dependency risk, fully sufficient for a sky gradient + campfire sprite + 50-100 dust particles. This can ship in the initial release.

Phase 2 — Rich Atmospheric Effects

Migrate to PixiJS v8 once the scene needs:

  • More than 200 particles
  • Animated water with displacement maps
  • Bloom/glow lighting effects
  • Multiple layered scenes tied to different Bible passages

Lazy-load PixiJS after page hydration using dynamic import() inside onMount. The ~280 KB gzip payload is acceptable when deferred and cached.

Animation Layer

Add GSAP (23 KB gzip) for scene transitions — fade, atmosphere shifts between passages — regardless of whether you use PixiJS or custom Canvas. Its easing system and timeline orchestration are worth the tiny cost.

Avoid

  • Phaser: too heavy, game-centric API
  • Three.js: 3D overhead, no pixel art tooling, worse tree-shaking
  • Fabric.js / Konva: designed for interactive editors, not scene rendering
  • p5.js: no tree-shaking, CPU-bound, not suited for production performance targets

Sources