Library / SDK
jonobr1/two.js avatar
jonobr1/two.js

Two.js: a renderer-agnostic 2D drawing API for modern browsers

A renderer agnostic two-dimensional drawing api for the web

8,661 stars463 forksJavaScriptMIT

At a glance

What is it?
A JavaScript library that provides a single API for drawing 2D graphics across multiple rendering contexts: WebGL, Canvas2D, and SVG. Use the same code to target different renderers without changes. Supports ES6 imports, headless Node.js rendering, and custom builds that exclude unneeded renderers.
Who is it for?
Adopt Two.js if you need to draw 2D graphics and want the flexibility to target multiple rendering backends without changing your code. Skip it if you already have a specific renderer in mind and do not need portability, or if you need 3D graphics (use Three.js instead).
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem: 2D graphics APIs are renderer-specific

Canvas2D is simple and widely supported but slow for complex scenes with many objects. WebGL is fast but requires shader code and boilerplate to set up vertex buffers and rendering pipelines. SVG scales infinitely without pixelation but is verbose and poorly suited for animation. Most projects pick one and commit to its API, so moving from Canvas2D to WebGL later requires rewriting the entire drawing code. Two.js solves this by providing a single, renderer-agnostic API that works identically across all three. You describe your scene once using shape creation methods, then tell Two.js which renderer to use via a configuration option. Switching from Canvas2D to WebGL later is a configuration change, not a code rewrite. This portability is useful for experiments where you do not know which renderer will work best, or for libraries that need to support multiple targets. The library has been solving this problem since 2012.

Renderer-agnostic scene graph API

Two.js exposes methods like `makeRectangle()`, `makeCircle()`, and `makePolygon()` that create shape objects. These objects can be rendered to WebGL, Canvas2D, or SVG without changing the creation code. The library maintains a scene graph: a tree of objects with transformations, and when you call `render()`, Two.js draws them in the chosen renderer. This abstraction matters because WebGL, Canvas2D, and SVG have completely different APIs. Canvas2D uses imperative drawing commands (`.drawRect()`, `.fillStyle`), WebGL requires shaders and vertex buffers for each shape, and SVG generates DOM elements or XML text. Two.js hides that complexity behind an object-oriented interface. You write drawing code once, and the library handles the backend translation. The scene graph supports transformations (position, rotation, scale), opacity, fill and stroke colors, clipping, and other standard drawing operations across all renderers. The same code that draws a red circle in WebGL draws it identically in Canvas2D or SVG.

Drawing a spinning rectangle with Two.js

Install Two.js from npm:

bash
npm install --save two.js

Or download the minified library and include it in your HTML:

html
<script src="js/two.min.js"></script>

Here is the HTML boilerplate to create a spinning rectangle:

html
<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <script src="js/two.min.js"></script>
  </head>
  <body>
    <script>
      var two = new Two({
        fullscreen: true,
        autostart: true
      }).appendTo(document.body);
      var rect = two.makeRectangle(two.width / 2, two.height / 2, 50 ,50);
      two.bind('update', function() {
        rect.rotation += 0.001;
      });
    </script>
  </body>
</html>

The `new Two()` constructor accepts options like `fullscreen` to fill the viewport and `autostart` to begin the animation loop. The `makeRectangle()` method creates a rectangle at the given x, y, width, and height. The `bind()` method attaches an update listener that fires on each frame. The default renderer is WebGL if available, falling back to Canvas2D. You can explicitly set the renderer with a `type` option: `{type: Two.Types.canvas}` for Canvas2D, `{type: Two.Types.svg}` for SVG. The scene automatically resizes with the window if fullscreen is true.

Using Two.js with React and modern frameworks

Two.js works with ES6 imports, so you can use it in React, Angular, or other bundled applications. The README includes a React example that uses `useEffect` to initialize Two.js when the component mounts and cleans up the renderer when it unmounts. Instead of passing `document.body` to `appendTo()`, you pass the DOM ref from React. This integration pattern works with webpack, esbuild, and other bundlers. The package includes TypeScript definitions in `src/two.d.ts` so IDEs provide autocomplete and type checking. As of version v0.7.5, Two.js is compatible with ES6 imports, making it a first-class citizen in modern JavaScript projects. The last release was v0.8.24 on 2026-08-29, with the previous release v0.8.21 on 2025-10-02, showing regular maintenance. The last push was on 2026-09-22, showing the project is actively maintained. The library has been in operation since 2012 and has a changelog documenting all changes from the initial alpha to the present.

Rendering in headless environments and custom builds

Two.js can also run on the server using Node Canvas, the library that brings HTML5 Canvas to Node.js. Install canvas with `npm install canvas`, then create a Two.js scene with a Canvas element, render it, and save the output as a PNG. The README includes an example that creates an 800x600 canvas, draws a rectangle with filled color, renders it, and saves the result to disk. This is useful for server-side image generation, dynamic graphics in APIs, and batch rendering. For client-side optimization, you can create custom builds. If your project only uses SVG rendering, you can modify `utils/build.js` to exclude the WebGL and Canvas2D renderers, then run `node ./utils/build` to generate a smaller bundle. This custom build approach can shrink the library significantly if you only need SVG output. The repository uses Node.js and npm for the build system, and the latest version is v0.8.24. TypeScript definitions are provided in `src/two.d.ts`, and tree shaking is on the roadmap for future versions to reduce bundle sizes further.

Limitations and when to use Three.js instead

Two.js is for 2D graphics only. If you need 3D rendering, use Three.js instead. Two.js does not yet support proper tree shaking, so importing the main `Two` namespace pulls in all modules; you cannot currently import only the Vector module and get a smaller bundle. The README mentions this is a future improvement planned for the roadmap. The library does not claim to handle extremely large scenes or provide collision detection or physics; it focuses on drawing and transformations. For interactive graphics with complex logic, you may need to add a physics or game framework on top. The main import will always load all renderers; custom builds are needed to reduce this. The WebGL renderer has performance limits compared to specialized game engines like Babylon.js or game-focused frameworks.

When to choose Pixi instead

Pixi.js is also a 2D renderer, but it is WebGL-only and optimized for sprite-based games and performance-critical scenes. Pixi renders faster than Two.js's WebGL mode for thousands of sprites because it is specialized. Two.js's advantage is flexibility: you can target Canvas2D, SVG, or WebGL with the same code, making it easier to support older browsers or vector-based workflows. Choose Pixi if you are building a game and need maximum performance; choose Two.js if you need portability across renderers. Two.js is better for creative coding, data visualization, and situations where you want to experiment with different backends without rewriting. Pixi is better for game development and performance-critical applications where every millisecond matters.

Editorial conclusion

Adopt Two.js if you need to draw 2D graphics and want the flexibility to target multiple rendering backends without changing your code. Skip it if you already have a specific renderer in mind and do not need portability, or if you need 3D graphics (use Three.js instead). Start with the boilerplate HTML example on two.js.org or read the examples and documentation to understand which renderer suits your use case best.

Frequently asked questions

What is Two.js?

Two.js is a JavaScript library for drawing 2D graphics. It provides a single API that can render to WebGL, Canvas2D, or SVG, so you can write drawing code once and target any renderer.

How is Two.js different from Three.js?

Two.js is for 2D graphics; Three.js is for 3D graphics. Two.js is renderer-agnostic (WebGL, Canvas2D, SVG); Three.js is WebGL-only. Choose Two.js for 2D, Three.js for 3D.

How is Two.js different from Pixi?

Pixi.js is WebGL-only and optimized for sprite-based games and performance. Two.js supports multiple renderers (WebGL, Canvas2D, SVG) and is better for portability across different targets.

How do I connect Two.js files in a project?

Install Two.js with `npm install --save two.js` or include the minified library with a script tag. Then import it as `import Two from 'two.js'` in ES6 modules or use the global `Two` variable if you included the script.

Can I use Two.js in Node.js?

Yes. Install the canvas module (`npm install canvas`) and use Two.js in a headless environment. You can create scenes, render them, and save output as PNG files for server-side image generation.

Official sources

  1. jonobr1/two.js on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/jonobr1-two-js.svg)](https://hysenlabs.com/projects/jonobr1-two-js)