Open-source project
xiaoiver/infinite-canvas-tutorial avatar
xiaoiver/infinite-canvas-tutorial

infinite-canvas-tutorial: building a GPU canvas one lesson at a time

A tutorial on infinite canvas

1,110 stars67 forksTypeScriptMIT

At a glance

What is it?
A tutorial that builds an infinite canvas editor in TypeScript, with a hardware abstraction layer over WebGL, WebGL2 and WebGPU, an entity component system, signed distance field rendering for shapes, a DOM-shaped event system, and a course of numbered lessons that each add one capability.
Who is it for?
The infinite-canvas-tutorial fits a front-end developer who wants to understand what a design tool is actually made of, layer by layer, and who is comfortable reading ten lessons of code that each assume the previous one. It is the wrong starting point for a team that needs a canvas in a product this month, because the finished artefact is a course rather than a product, and the README says the project completion is still relatively low.
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 received new commits within the last day.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

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

Editorial analysis

What the author wanted to understand

The premise is a question about rendering. The author is a front-end developer who is interested in the rendering technologies behind infinite canvas products, and who observes that the well-known tools mostly use friendlier technologies like Canvas2D and SVG, while a number of editors and design tools in the JavaScript and Rust ecosystems use lower-level rendering for 2D graphics with GPU acceleration, for better performance and experience.

The examples given are specific and they are the reason the project exists. Figma uses a tile-based rendering engine written in C++, compiled into WebAssembly, and then calls WebGL and WebGPU for rendering. Motiff uses a tile-based rendering engine with WebGL. Modyfi uses wgpu from the Rust ecosystem, also compiled into WebAssembly, and then calls WebGL2. Zed uses GPUI to render rectangles, shadows, text and images. And Vello and xilem experimentally use compute shaders for 2D rendering.

The definition of an infinite canvas that the tutorial starts from comes from a glossary, and it has three parts. High scalability, so users can organise content structures in a non-linear fashion. Zooming, which emulates zooming in to get an overview and zooming out to observe details as in the real world. And direct manipulation, which provides intuitive editing for basic shapes, including moving, grouping and modifying styles.

The third part is where the difficulty is. A canvas where you can pan, zoom, group and style shapes has to answer the same question every time the user moves something: which shapes are on screen, and which of them did the pointer touch. The ten lessons are an answer to that question, built in public, in order.

The stack: one abstraction layer, an ECS, and signed distance fields

The features the tutorial sets out to implement are listed in the README, and the list is the architecture.

The hardware abstraction layer is `@antv/g-device-api`, and it supports WebGL1, WebGL2 and WebGPU, so the same code can be tested on whichever backend the browser gives you. The scene model is `Becsy`, an entity component system for high-performance, highly scalable systems, and the ECS is the right shape for a canvas because a canvas is a graph of objects with a changing set of properties, and an ECS gives that a data-oriented home.

The shapes are drawn with SDF, signed distance field rendering, for circles, ellipses and rectangles. That choice buys antialiasing and resolution independence for free, and it is also what makes the later lessons possible, because a drop shadow or an inner shadow is a modification of the distance function rather than a separate pass.

Text and Bezier curves are GPU accelerated, the hand-drawn look comes from `rough.js`, collaboration is handled with a CRDT, a conflict-free replicated data type, driving Yjs, and the rendering is planned to be tile based, referencing Mapbox and Figma for the approach.

The README also explains why the author wants to rewrite the rendering part in Rust eventually, and the reasons are specific rather than ideological. wgpu is a very reliable hardware abstraction layer, it can even implement the backend for piet, shaders can basically be reused, the hand-drawn styles can use rough-rs, and y-crdt is the Rust implementation of Yjs. So the plan is not a rewrite in a different mood, it is the same design with the pieces swapped for the Rust equivalents, and the tutorial is where the design is written down first.

Ten lessons, and the dirty flag in the second one

The course is the deliverable, and the lesson list is the table of contents.

Lesson 1 initialises the canvas: a hardware abstraction layer over WebGL1, WebGL2 and WebGPU, the design of the canvas API, a simple plugin system, and a rendering plugin built on the abstraction layer. Lesson 2 draws a circle with SDF, adds antialiasing, and introduces the dirty flag design pattern. Lesson 3 adds transformations, so shapes support pan, zoom, rotate and skew, and a scene graph. Lesson 4 is the camera, what it is, the projection transformation, the camera transformation, and camera animation using a landmark transition between camera states. Lesson 5 is the grid, drawing straight lines with line geometry or with screen-space techniques, a dots grid, and a wireframe for geometry.

Lesson 6 is the interaction layer: an event system compatible with the DOM Event API, how to pick a circle, a drag and drop plugin built on that event system, and support for pinch zoom gestures. Lesson 7 builds the web interface with Lit and Shoelace, including a canvas component, a zoom toolbar component, a dark theme, and internationalisation with Lit localization. Lesson 8 is the performance lesson, covering what a draw call is, reducing draw calls with culling, reducing them further by combining batches, and using spatial indexing to improve picking efficiency. Lesson 9 derives the SDF representation of an ellipse or a rounded rectangle, renders drop shadows and inner shadows for SDF, and determines whether a point is inside an ellipse or a rounded rectangle. Lesson 10 covers importing and exporting images.

The lesson that matters for anybody joining an existing canvas project is lesson 2, because the dirty flag is the first structural decision a canvas makes. Draw everything, every frame, is simple and slow. Mark the region that changed, and redraw only that, is the arrangement every implementation converges on, and the tutorial puts it in the second lesson for that reason.

Picking, dragging and an event system shaped like the DOM

Two of the lessons are about the parts of a canvas that are not graphics, and they are the parts that separate a drawing from an editor.

The event system is the first. It is implemented to be compatible with the DOM Event API, and that compatibility is the design decision. A canvas that draws pixels has no elements, so every piece of interface on it, a toolbar, a context menu, a selected shape badge, has to be implemented, and if the events do not look like the ones a browser developer already knows, every existing pattern has to be relearned. Making the event system look like the DOM's means that event delegation, capture, bubbling and the usual listener patterns work as they do on a page, and the drag and drop plugin is then written the way any other web drag and drop is written.

The second is picking, that is, how to pick a circle, and the answer is where spatial indexing comes in. A canvas holds shapes at arbitrary coordinates under an arbitrary transform, and a click has to become a shape. Doing that by testing every shape on every click is fine for twenty shapes and hopeless for twenty thousand, which is why lesson 8 returns to the same problem with an index, and why the spatial indexing in the performance lesson is described as improving pickup efficiency rather than as a general acceleration trick.

Pinch zoom is the third piece in this group, and it is a real one, because a canvas that is only usable with a mouse is not usable on a tablet, and the gesture has to be reconciled with the camera transform and with the pointer picking at the same time.

Draw calls, culling and batches

Lesson 8 is the one a working engineer will read first, because the performance of a canvas is a rendering problem before it is an application problem.

It starts with the definition, what a draw call is, and then gives two ways to reduce them. Culling removes work: shapes that are off screen do not need to be submitted. Combining batches reduces the cost of the work that remains, by grouping what can be grouped.

The third tool, spatial indexing, is not about the GPU at all, it is about the CPU, and the lesson places it under performance because a canvas spends its time in two places. A canvas that draws quickly but responds slowly to a click feels broken, and a canvas that clicks fast but draws slowly feels broken in a different way, and the same index serves both if it is the index the picking code consults.

The screenshots in the repository for this lesson, and for the geometry one, show the two grid renderings and the two shadow renderings side by side, which is the useful part of a graphics tutorial: the difference between a line drawn with line geometry and a line drawn with a screen-space technique, and the difference between a drop shadow and an inner shadow, are both things you can only really understand by seeing them next to each other.

Running it, and the Rust plan underneath

The course is a pnpm workspace, so the setup is short.

bash
pnpm i

Then, in the course directory, the Vite dev server for a lesson:

bash
cd packages/lesson_001
pnpm run dev

Or the site itself, built and served:

bash
pnpm run build
cd packages/site
pnpm run dev

The deployment script pushes the built documentation to Cloudflare Pages with `npx wrangler pages deploy`, which tells you the documentation site is a VitePress build inside the site package, and the lesson links all point at `infinitecanvas.cc/guide/lesson-00x`.

If you want to use the code in your own project, the README points at two published packages, `@infinite-canvas-tutorial/ecs` and `@infinite-canvas-tutorial/webcomponents`, which is the sensible part to take, because the lesson code is teaching material and the packages are the reusable parts.

The Rust plan is already in the repository rather than in a document. There is a `rust/` directory with a vello renderer, and the build script compiles it with `cargo build --release --target wasm32-unknown-unknown`, then runs wasm-bindgen over the output and writes a package manifest for it. A second variant uses wasm-pack instead. So the WebAssembly path the author describes as the future is a working script in the project today, which is the honest way to present a plan like this.

The testing setup is broad for a course. There are several Playwright configurations, for the browser, for the user interface, for React, for WebKit and for React on WebKit, and Jest configurations for the ECS and for the React parts, with a coverage badge and a dedicated workflow for the unit tests.

Where this tutorial is the wrong thing

The limits are the limits of a course, and the README is explicit about them.

The author says the current project completion is still relatively low, and that the hope is to rewrite the rendering part of the canvas in Rust in the future. So the artefact you get today is a set of lessons that build up to a working canvas, not a canvas you would ship. That is the right shape for a tutorial and the wrong shape for a dependency, and the difference is not a matter of polish.

The second limit is the dependency stack. The hardware abstraction layer, the ECS, SDF rendering, a DOM-compatible event system, a CRDT for collaboration, a rough rendering library, tile-based rendering, and a WebGPU target is a lot of machinery to assemble before a shape can be drawn. A team that needs a canvas in a product this month is better served by one of the products the tutorial lists, and the tutorial is honest about which side of that line it sits on.

The third is the target. WebGPU is where the interesting work is, but it is not everywhere, and a hardware abstraction layer over three backends is more code than a canvas that targets one. The abstraction is the right answer for a library and the wrong answer for an application, and the tutorial is building a library.

So the sensible use of this repository is as a map. It is a working answer to the question of what a canvas is made of, divided into ten pieces, each of which is small enough to read in an evening, with the screenshots for the parts where seeing is the point. For somebody who wants to build one, the two published packages and the lesson structure are the take-away, and the Rust renderer is the direction of travel for anybody who intends to go further.

Editorial conclusion

The infinite-canvas-tutorial fits a front-end developer who wants to understand what a design tool is actually made of, layer by layer, and who is comfortable reading ten lessons of code that each assume the previous one. It is the wrong starting point for a team that needs a canvas in a product this month, because the finished artefact is a course rather than a product, and the README says the project completion is still relatively low. Before committing an afternoon, read the lesson list end to end, because the first three lessons are the expensive ones and the rest is incremental, and decide early whether the WebGPU target is one you can actually ship to, since the hardware abstraction layer is the piece everything else sits on.

Frequently asked questions

What is the infinite-canvas-tutorial project?

It is a tutorial that builds an infinite canvas editor in TypeScript, published as a course site at infinitecanvas.cc with a pnpm workspace of numbered lessons, an online demo, and two reusable packages, @infinite-canvas-tutorial/ecs and @infinite-canvas-tutorial/webcomponents.

Which rendering technologies does the tutorial use?

The @antv/g-device-api hardware abstraction layer over WebGL1, WebGL2 and WebGPU, Becsy for the entity component system, SDF rendering for shapes, GPU accelerated text and Bezier curves, rough.js for hand-drawn styles, and CRDTs driving Yjs for collaboration, with tile-based rendering planned after a reference to Mapbox and Figma.

How do I run the infinite canvas tutorial locally?

Install pnpm and run `pnpm i` in the course directory, then `cd packages/lesson_001` with `pnpm run dev` for the Vite dev server. To run the site instead, use `pnpm run build` and then `cd packages/site` with `pnpm run dev`.

Is the infinite canvas tutorial finished?

The README says the current project completion is still relatively low, and the plan is to rewrite the rendering part of the canvas in Rust using wgpu, reusable shaders, rough-rs for the hand-drawn styles and y-crdt as the Rust implementation of Yjs. A Rust renderer already exists in the repository with cargo and wasm-bindgen scripts to build it.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. xiaoiver/infinite-canvas-tutorial on GitHub
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/xiaoiver-infinite-canvas-tutorial.svg)](https://hysenlabs.com/projects/xiaoiver-infinite-canvas-tutorial)

Was this useful?