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.
The BibleWeb app needs immersive, atmospheric backgrounds for Bible passages. These are decorative scene layers beneath the reading UI, not interactive games. Requirements:
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.
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)@pixi/react and svelte-pixi community wrappers availableSvelteKit 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:
Cons:
svelte-pixi wrapper lags behind PixiJS releasesVerdict: Strong recommendation. PixiJS is the professional choice for this feature set.
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/quadsPoints + BufferGeometry for custom particle systemsthree/addons/postprocessing)NearestFilter texture filteringBundle 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:
Cons:
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.
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 modelnoise() function (Perlin noise) for organic particle movement and cloud generationcreateVector() for particle physicsloadImage() + 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:
Cons:
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.
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:
Cons:
Verdict: Not recommended for decorative backgrounds. The bundle cost and conceptual overhead are not justified unless BibleWeb expands into interactive animated content.
These libraries serve a different primary purpose: interactive canvas drawing with selectable, draggable shapes — think diagram editors and design tools.
Konva.js:
svelte-konva package available with SvelteKit supportFabric.js:
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).
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:
requestAnimationFrameLimitations 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.
Building a custom renderer means writing directly against the Canvas 2D API or WebGL, with no library overhead.
When custom beats libraries:
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:
Pros:
Cons:
ctx.shadowBlur or multi-pass compositingVerdict: Start here for MVP. If you need more than ~500 particles, displacement maps, or GPU-backed effects, migrate to PixiJS.
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:
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.
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:
onMount — never initialize canvas at module scope (breaks SSR)import() inside onMount keeps the graphics library out of the SSR bundle entirelyonDestroy to clean up the renderer and free GPU memorybind:this to get a reference to the canvas elementpointer-events-none on the canvas so it does not intercept Bible text interactionsprefers-reduced-motion: skip animation or use a static image fallback| 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) |
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.
Migrate to PixiJS v8 once the scene needs:
Lazy-load PixiJS after page hydration using dynamic import() inside onMount. The ~280 KB gzip payload is acceptable when deferred and cached.
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.