Skip to content

ahmerhabib/WebGraphicLibrary-fixedbaseoperator

Repository files navigation

WebGraphicLibrary — Fixed Base Operator (FBO)

Type-safe WebGL Framebuffer Object wrapper for off-screen rendering.

CI npm version License: MIT TypeScript Node >=18 Last Commit Open Issues


Screenshot / Demo

+----------------------------------------------------------+
|                  Off-screen Render Pass                  |
|                                                          |
|   +---------------------+    +---------------------+    |
|   |   FBO (512 x 512)   |---►|  Screen Framebuffer |    |
|   |                     |    |                     |    |
|   |  colour texture ----+---►|  post-process quad  |    |
|   |  depth buffer   ----+    |  (fbo.texture)      |    |
|   +---------------------+    +---------------------+    |
|                                                          |
|  fbo.bind()  ->  draw scene  ->  fbo.unbind()           |
+----------------------------------------------------------+

A real interactive demo will be added in a future release.


Table of Contents


About

WebGraphicLibrary FBO is a lightweight, zero-dependency TypeScript library that wraps the verbose WebGL framebuffer API into a clean, ergonomic interface.

A Framebuffer Object (FBO) redirects GPU draw calls away from the screen and into a texture. That texture can then be sampled by another shader, post-processed, or read back to the CPU — forming the backbone of techniques like:

  • Shadow mapping
  • Reflections and refractions
  • Screen-space ambient occlusion (SSAO)
  • Bloom, blur, and other post-processing effects
  • Deferred rendering
  • Screen capture / export to image

The library handles the full object lifecycle — creation, completeness validation, bind/unbind state, optional depth attachment, resize, pixel readback, and safe disposal — with full TypeScript types throughout.


Features

  • Zero runtime dependencies — fully self-contained
  • TypeScript-first — ships .d.ts declarations for every export
  • WebGL and WebGL2 supported via one unified API
  • Optional depth / depth+stencil renderbuffer attachment
  • resize() — updates texture storage in-place, stable framebuffer reference
  • readPixels() — convenience method to pull rendered data back to the CPU
  • Framebuffer completeness validation — descriptive error if the driver reports incomplete status
  • Disposal guard — prevents use-after-free and double-dispose
  • Debug logging — opt-in via FBO_DEBUG environment variable; zero cost in production
  • Dual ESM + CJS output — works with every modern bundler and Node.js

Tech Stack

Tool Version Purpose
TypeScript 5.x Type-safe source language
Rollup 4.x Library bundler (ESM + CJS output)
Vitest 3.x Unit testing framework
ESLint 9.x Static analysis (flat config)
Prettier 3.x Opinionated code formatting
GitHub Actions CI/CD pipeline

Getting Started

Prerequisites

  • Node.js >= 18
  • A project that targets a WebGL-capable browser (the FBO class is a browser runtime library)

Installation

npm install @ahmerhh/webgraphiclibrary-fixedbaseoperator
# yarn
yarn add @ahmerhh/webgraphiclibrary-fixedbaseoperator

# pnpm
pnpm add @ahmerhh/webgraphiclibrary-fixedbaseoperator

Quick Start

import { FBO } from '@ahmerhh/webgraphiclibrary-fixedbaseoperator';

const canvas = document.getElementById('canvas') as HTMLCanvasElement;
const gl     = canvas.getContext('webgl')!;

// Create a 512 x 512 off-screen render target
const fbo = new FBO(gl, 512, 512);

// Render into the FBO
fbo.bind();
gl.clear(gl.COLOR_BUFFER_BIT);
// ...your draw calls...
fbo.unbind();

// fbo.texture is now ready to be sampled in a shader

// Free GPU resources when done
fbo.dispose();

API Reference

Constructor

new FBO(gl, width, height, options?)
Parameter Type Required Description
gl WebGLRenderingContext | WebGL2RenderingContext Yes Active WebGL context
width number Yes Framebuffer width in pixels (positive integer, max 8192)
height number Yes Framebuffer height in pixels (positive integer, max 8192)
options Partial<FBOOptions> No Texture format and attachment configuration

Throws:

Error type Condition
TypeError gl is not a valid WebGL context
TypeError width or height is not a positive integer
RangeError Either dimension exceeds 8192
Error WebGL driver fails to allocate framebuffer, texture, or renderbuffer
Error gl.checkFramebufferStatus returns a non-complete status

Properties

Property Type Description
gl GLContext The WebGL context provided at construction time
width number Current framebuffer width — updated by resize()
height number Current framebuffer height — updated by resize()
framebuffer WebGLFramebuffer The underlying WebGL framebuffer object
texture WebGLTexture The colour attachment — bind this in a shader
renderbuffer WebGLRenderbuffer | null Depth/stencil renderbuffer (null if not requested)
disposed boolean true after dispose() has been called

Methods

bind(): void

Makes this FBO the active render target. All subsequent draw calls write into fbo.texture.

fbo.bind();
gl.drawArrays(gl.TRIANGLES, 0, 6);
fbo.unbind();

Throws Error if called on a disposed FBO.

unbind(): void

Restores the default (screen) framebuffer. Equivalent to gl.bindFramebuffer(gl.FRAMEBUFFER, null).

resize(width, height): void

Re-allocates the texture (and depth renderbuffer if present) at new dimensions. The fbo.framebuffer reference remains stable — no need to rebind uniforms.

window.addEventListener('resize', () => {
  fbo.resize(canvas.width, canvas.height);
});

readPixels(): Uint8Array

Reads the current pixel data from the colour attachment into a Uint8Array of length width * height * 4 (RGBA). Requires the default RGBA / UNSIGNED_BYTE format.

const pixels = fbo.readPixels();
const [r, g, b, a] = pixels; // bottom-left pixel

dispose(): void

Deletes all GPU objects (framebuffer, texture, renderbuffer). Safe to call multiple times — subsequent calls are no-ops. The disposed getter returns true afterwards.


FBOOptions

interface FBOOptions {
  internalFormat?: number;   // default: gl.RGBA
  format?:         number;   // default: gl.RGBA
  type?:           number;   // default: gl.UNSIGNED_BYTE
  minFilter?:      number;   // default: gl.LINEAR
  magFilter?:      number;   // default: gl.LINEAR
  wrapS?:          number;   // default: gl.CLAMP_TO_EDGE
  wrapT?:          number;   // default: gl.CLAMP_TO_EDGE
  depth?:          boolean;  // default: false — attach a DEPTH_COMPONENT16 renderbuffer
  stencil?:        boolean;  // default: false — attach a combined DEPTH_STENCIL renderbuffer
}

Examples

Basic Off-Screen Rendering

import { FBO } from '@ahmerhh/webgraphiclibrary-fixedbaseoperator';

const fbo = new FBO(gl, 512, 512);

function renderFrame(): void {
  // Off-screen pass
  fbo.bind();
  gl.viewport(0, 0, fbo.width, fbo.height);
  gl.clearColor(0, 0, 0, 1);
  gl.clear(gl.COLOR_BUFFER_BIT);
  renderScene();
  fbo.unbind();

  // Screen pass — use fbo.texture as a shader input
  gl.viewport(0, 0, canvas.width, canvas.height);
  gl.bindTexture(gl.TEXTURE_2D, fbo.texture);
  renderFullscreenQuad();

  requestAnimationFrame(renderFrame);
}

With Depth Buffer

const fbo = new FBO(gl, 1024, 1024, { depth: true });

Dynamic Resize on Window Resize

const fbo = new FBO(gl, canvas.width, canvas.height);

window.addEventListener('resize', () => {
  canvas.width  = window.innerWidth;
  canvas.height = window.innerHeight;
  fbo.resize(canvas.width, canvas.height);
});

Reading Pixels Back to CPU

// Useful for thumbnails, pixel-picking, or image export
fbo.bind();
renderScene();
fbo.unbind();

const pixels = fbo.readPixels(); // Uint8Array, RGBA

WebGL2 — Floating-Point Render Target

const gl2 = canvas.getContext('webgl2')!;

const hdrFbo = new FBO(gl2, 512, 512, {
  internalFormat: gl2.RGBA16F,
  format:         gl2.RGBA,
  type:           gl2.FLOAT,
});

Configuration

Copy .env.example to .env and adjust as needed:

cp .env.example .env
Variable Default Description
FBO_DEBUG (unset) Set to any truthy string to enable library debug logging
NODE_ENV development Affects test and build behaviour

In browser contexts, set window.__FBO_DEBUG__ = true before importing the library.


Development

# Install dependencies
npm install

# Start Rollup watcher
npm run dev

# Lint
npm run lint
npm run lint:fix

# Format
npm run format:check
npm run format

# Type-check (no emit)
npm run typecheck

# Full pre-publish check (lint + typecheck + test + build)
npm run prepublishOnly

Testing

# Run tests once
npm test

# Watch mode
npm run test:watch

# With coverage report
npm run test:coverage

Coverage thresholds enforced in CI:

Metric Threshold
Lines 80%
Functions 80%
Branches 75%
Statements 80%

Docker

# Run the full test suite
docker compose run test

# Build the dist/ artefacts (exported to ./dist on the host)
docker compose run build

# Start Rollup in watch mode with sources mounted
docker compose run dev

Contributing

Contributions are welcome!

  1. Fork this repository
  2. Create a feature branch: git checkout -b feat/my-feature
  3. Make your changes and add tests
  4. Verify everything passes: npm run prepublishOnly
  5. Open a pull request with a clear description

Please follow the existing code style (enforced by ESLint + Prettier) and add tests for any new behaviour.


License

MIT — see LICENSE.md for full details.


Made with care by ahmerhh

About

Web Graphic Library (wGL) Render Target Encapsulation

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors