Home

A JavaScript framework for creating 2D WebGL2 applications

Game Demo

Demos:

Features:

  • Batch rendering (10.000 elements - 60fps)
  • Dynamic 2D lights and shadows
  • Element picker (can click on rendered items)
  • Image filters (Blur, Pixelate, Distortion, etc.)
  • Video textures
  • and many other features...

Minified version

How to use

Create your index html ( include pwgl.min.js )

<!DOCTYPE html>
<html>
  <head>
    <script src="pwgl.min.js" type="text/javascript"></script>
  </head>
  <body></body>
</html>

Add your script

class Application {
  constructor() {}
}
PWGL.Utils.initApplication(function (isWebGl2Supported) {
  if (!isWebGl2Supported) {
    // WebGL 2 is not supported
    return;
  }

  new Application();
});

Create a simple 2d renderer environment

class Application {
  constructor() {
    const width = 800;
    const height = 600;

    this._stageContainer = document.body;

    // create context
    this._context = new PWGL.Context();

    // create stage 2d renderer
    this._stage2DRenderer = new PWGL.Stage2D({
      config: {
        context: this._context,
      },
    });

    this._stageContainer.appendChild(this._context.canvas);

    // create renderable element
    this._image = new PWGL.Image(
      PWGL.Texture.loadImage("your/image/path/here")
    );
    this._image.props.x = width * 0.5;
    this._image.props.y = height * 0.5;
    this._image.props.width = 320;
    this._image.props.height = 240;
    this._image.props.anchorX = this._image.props.anchorY = 0.5;
    this._stage2DRenderer.container.addChild(this._image);

    // resize context and renderers
    this._context.setCanvasSize(width, height);
    this._stage2DRenderer.setSize(width, height);

    this._onBeforeUnloadBound = this._onBeforeUnload.bind(this);
    this._renderBound = this._render.bind(this);
    this._requestAnimationFrameId;

    window.addEventListener("beforeunload", this._onBeforeUnloadBound);

    // set fps meter
    PWGL.FPS.start(60);

    // start render cycle
    this._requestAnimationFrameId = requestAnimationFrame(this._renderBound);
  }

  _render() {
    if (this._requestAnimationFrameId) {
      PWGL.FPS.update();
      let delay = PWGL.FPS.delay;

      console.log("delay:", PWGL.FPS.delay);
      console.log("fps:", PWGL.FPS.fps.toFixed(2));

      // rotate the image
      this._image.props.rotation += 0.001;

      // render the state
      this._stage2DRenderer.render();

      this._requestAnimationFrameId = requestAnimationFrame(this._renderBound);
    }
  }

  _destruct() {
    cancelAnimationFrame(this._requestAnimationFrameId);
    window.removeEventListener("beforeunload", this._onBeforeUnloadBound);
    this._stageContainer.removeChild(this._context.canvas);
    this._stage2DRenderer.destruct();
  }

  _onBeforeUnload() {
    this._destruct();
  }
}

PWGL.Utils.initApplication(function (isWebGl2Supported) {
  if (!isWebGl2Supported) {
    // WebGL 2 is not supported
    return;
  }

  new Application();
});

Add filter renderer

class Application {
  constructor() {
    const width = 800;
    const height = 600;

    this._stageContainer = document.body;

    // create context
    this._context = new PWGL.Context();

    // create framebuffer for the stage 2d renderer
    this._stage2DRendererFramebuffer = new PWGL.Framebuffer();

    // create stage 2d renderer
    this._stage2DRenderer = new PWGL.Stage2D({
      config: {
        context: this._context,
      },
    });

    // create filter renderer and set the framebuffer as texture source
    this._filterRenderer = new PWGL.FilterRenderer({
      config: {
        context: this._context,
      },
      sourceTexture: this._stage2DRendererFramebuffer,
      filters: [
        new PWGL.PixelateFilter(5),
        new PWGL.VignetteFilter(1, 3, 1, 0, 0, 0),
      ],
    });

    this._stageContainer.appendChild(this._context.canvas);

    // create renderable element
    this._image = new PWGL.Image(
      PWGL.Texture.loadImage("your/image/path/here")
    );
    this._image.props.x = width * 0.5;
    this._image.props.y = height * 0.5;
    this._image.props.width = 320;
    this._image.props.height = 240;
    this._image.props.anchorX = this._image.props.anchorY = 0.5;
    this._stage2DRenderer.container.addChild(this._image);

    // resize context and renderers
    this._context.setCanvasSize(width, height);
    this._stage2DRenderer.setSize(width, height);
    this._filterRenderer.setSize(width, height);

    this._onBeforeUnloadBound = this._onBeforeUnload.bind(this);
    this._renderBound = this._render.bind(this);
    this._requestAnimationFrameId;

    window.addEventListener("beforeunload", this._onBeforeUnloadBound);

    // set fps meter
    PWGL.FPS.start(60);

    // start render cycle
    this._requestAnimationFrameId = requestAnimationFrame(this._renderBound);
  }

  _render() {
    if (this._requestAnimationFrameId) {
      PWGL.FPS.update();
      let delay = PWGL.FPS.delay;

      console.log("delay:", PWGL.FPS.delay);
      console.log("fps:", PWGL.FPS.fps.toFixed(2));

      // rotate the image
      this._image.props.rotation += 0.001;

      // render the state to framebuffer
      this._stage2DRenderer.renderToFramebuffer(
        this._stage2DRendererFramebuffer
      );
      // render filters
      this._filterRenderer.render();

      this._requestAnimationFrameId = requestAnimationFrame(this._renderBound);
    }
  }

  _destruct() {
    cancelAnimationFrame(this._requestAnimationFrameId);
    window.removeEventListener("beforeunload", this._onBeforeUnloadBound);
    this._stageContainer.removeChild(this._context.canvas);
    this._stage2DRenderer.destruct();
    this._filterRenderer.destruct();
  }

  _onBeforeUnload() {
    this._destruct();
  }
}

PWGL.Utils.initApplication(function (isWebGl2Supported) {
  if (!isWebGl2Supported) {
    // WebGL 2 is not supported
    return;
  }

  new Application();
});