Profile
Back to NewsBack
GitHub Trending 12 min
Reader Mode
xiaoiver/infinite-canvas-tutorial: A tutorial on infinite canvas

xiaoiver/infinite-canvas-tutorial: A tutorial on infinite canvas

14 hours ago

An Infinite Canvas Tutorial

Infinite canvas tutorial is released under the MIT license</a> PRs welcome!</a> Build Status</a> Coverage Status</a>

[Online App] | [Self-Deployment Guide]

[My free course in Gumroad]. Feel free to rate!

ko-fi</a>

Open in StackBlitz</a>

What is an Infinite Canvas? The term "infinite" in [infinitecanvas] is described as follows:

  • High scalability. Users can freely organize content structures in a non-linear fashion.
  • Zooming. Emulates the "zoom in" to get an overview and "zoom out" to observe details as in the real world.
  • Direct manipulation. Provides intuitive editing capabilities for basic shapes, including moving, grouping, and modifying styles.
The [infinitecanvas] showcases numerous examples ranging from design tools to creative boards, including some well-known products such as: [Figma], [Modyfi], [Motiff], [rnote], [tldraw], [excalidraw] and so on.

As a front-end developer, I am very interested in the rendering technologies involved. Although tldraw, excalidraw, and others generally use more user-friendly technologies like Canvas2D/SVG, there are also many editors and design tools in the JS and Rust ecosystems that use more low-level rendering technologies for 2D graphics with GPU acceleration to achieve better performance and experience:

  • [Figma] uses a tile-based rendering engine written in C++, compiled into WASM and then calls WebGL and WebGPU for rendering.
  • [Motiff] also uses a tile-based rendering engine with WebGL.
  • [Modyfi] uses [wgpu] from the Rust ecosystem, also compiled into WASM and then calls WebGL2 for rendering.
  • [Zed] uses GPUI to render rectangles, shadows, text, images, and other UI elements.
  • [Vello] and [xilem] experimentally use Compute Shader for 2D rendering.
Therefore, in this tutorial, I hope to implement the following features:
  • Use [@antv/g-device-api] as a hardware abstraction layer, supporting WebGL1/2 and WebGPU.
  • Use [Becsy] to implement high-performance, highly scalable systems based on the ECS architecture.
  • Use SDF (Signed Distance Field) rendering for circles, ellipses, rectangles, etc.
  • GPU-accelerated text and Bezier curve rendering.
  • Use [rough.js] to support hand-drawn styles.
  • Use CRDT (Conflict-free Replicated Data Type) to support collaborative [Yjs].
  • Referencing [mapbox] and [Figma], attempt to use tile-based rendering.
I hope to rewrite the rendering part of the canvas with Rust in the future, but the current project completion is still relatively low:
  • [wgpu] is a very reliable hardware abstraction layer, which can even implement the backend for [piet].
  • Shaders can basically be reused.
  • Hand-drawn styles can use [rough-rs].
  • [y-crdt] is the Rust implementation of [Yjs].
Let's get started!

Getting Started

The course project uses pnpm workspace, so you need to install pnpm first.

pnpm i

After entering the course directory, run Vite devserver:

cd packages/lesson_001
pnpm run dev

Or you can run the site locally:

pnpm run build
cd packages/site
pnpm run dev

If you want to use it in your own project, you can refer to:

  • [@infinite-canvas-tutorial/ecs]
  • [@infinite-canvas-tutorial/webcomponents]

Lesson 1 - Initialize canvas 🔗

  • A hardware abstraction layer based on WebGL1/2 and WebGPU.
  • Canvas API design.
  • Implementing a simple plugin system.
  • Implementing a rendering plugin based on the hardware abstraction layer.

Lesson 2 - Draw a circle 🔗

  • Adding shapes to the canvas.
  • Drawing a circle using SDF.
  • Anti Aliasing.
  • Dirty flag design pattern.

Lesson 3 - Scene graph and transform 🔗

  • Transformations. Make shapes support pan, zoom, rotate, and skew transformations.
  • Scene graph.
Lesson 3

Lesson 4 - Camera 🔗

  • What is a Camera?
  • Projection transformation.
  • Camera transformation.
  • Camera animation. Using Landmark transition between different camera states.

Lesson 5 - Grid 🔗

  • Drawing straight lines using Line Geometry or screen-space techniques.
  • Drawing dots grid.
  • Drawing wireframe for Geometry.
| Grid | Wireframe | | :--------------------------------: | :---------------------------------------: | | !Grid | !Wireframe |

Lesson 6 - Event system 🔗

  • Implement an event system compatible with DOM Event API.
  • How to pick a circle.
  • Implement a drag-and-drop plugin based on our event system.
  • Support for pinch zoom gestures.

Lesson 7 - Web UI 🔗

  • Developing Web UI with Lit and Shoelace
  • Implementing a canvas component
  • Implementing a zoom toolbar component
  • Implementing a dark theme
  • I18n with Lit localization

Lesson 8 - Optimize performance 🔗

  • What is a draw call
  • Reducing draw calls with culling
  • Reducing draw calls by combining batches
  • Using spatial indexing to improve pickup efficiency
Lesson 8

Lesson 9 - Draw ellipse and rectangle 🔗

  • How to derive the SDF representation of an ellipse or rounded rectangle
  • Render drop-shadow and inner shadow for SDF
  • How to determine if a point is inside an ellipse or rounded rectangle
| Drop Shadow | Inner Shadow | | :---------------------------------------: | :------------------------------------------: | | !Drop Shadow | !Inner Shadow |

Lesson 10 - Import and export images 🔗

  • Exporting canvas content to PNG, JPEG and SVG formats
  • Rendering images in the canvas
  • Extending the capabilities of SVG, using stroke-alignment as an example
  • CSS object-fit and object-position on image fills (fill, contain, or cover)
Lesson 10 - import and export images

Lesson 11 - Test and server-side rendering 🔗

  • Jest-based test environment setup, including local and CI environments
  • Using unit tests to improve code coverage
  • Visual regression testing
- Server-side rendering based on headless-gl, targets WebGL1 - E2E testing base on Playwright, targets WebGL2 & WebGPU
  • E2E UI testing
  • Browser Compatibility Test based on BrowserStack
  • Render in WebWorker

Lesson 12 - Draw polyline 🔗

  • Why not just use gl.LINES?
  • Building Mesh in the CPU or Shader
  • Building segments, caps and joints, antialiasing, and drawing dashed lines in shader
  • How to calculate its bounding box?
Lesson 12 - polyline

Lesson 13 - Draw path and sketchy style 🔗

  • Experimenting with SDF
  • Trying to draw fills using some triangulating methods and strokes using polylines
- Support earcut and libtess.js two triangulation schemes - Handle holes in the path correctly - Support fillRule property
  • Draw some hand-drawn shapes
  • Watercolor effect
| Path and rough shapes | Fill rule | Watercolor | | :--------------------------------------------------: | :----------------------------------------: | :-----------------------------------------: | | !Path and rough shapes | !Fill rule | !watercolor |

Lesson 14 - Canvas mode and auxiliary UI 🔗

  • Implement zIndex and sizeAttenuation
  • Add more canvas modes, e.g. move and select and shapes
Lesson 14 - canvas mode

Lesson 15 - Text rendering 🔗

  • What's TextMetrics and how to get it in server and browser side
  • What's shaping? Implement letterSpacing and kerning
  • Paragraph layout
- Auto wordbreak - BiDi, arabic shaping included - Handle clusters - Support text-align
  • How to generate SDF atlas and use it to draw
  • How to use ESDT and MSDF to improve text rendering quality
  • How to draw bitmap font
  • How to draw emoji
| Render text with SDF atlas | Bitmap font, emoji, BiDi and clusters | | :-----------------------------------------------------------: | :--------------------------------------------: | | !Linear, Radial, Conic Gradient | !Mesh Gradient |

Lesson 16 - Text advanced features 🔗

  • Using Bezier curves to render text, shaping with OpenType and Harfbuzz
  • Render TeX math
  • Text stroke, decoration, dropshadow and text along path
  • Physical text rendering
  • Load web font with web font loader
| Shaping TeX with MathJax | Physical text | | :-----------------------------------------------------: | :--------------------------------------------: | | !Shaping TeX with MathJax | !physical text |

| Text decoration | Text stroke | | :-----------------------------------------------------: | :------------------------------------------: | | !Text decoration - wavy | !Text stroke |

Lesson 17 - Gradient and Pattern 🔗

  • Use CanvasGradient to implement gradients
- Imperative. Create textures using the Device API - Declarative. Supports CSS gradient syntax: linear-gradient, radial-gradient, conic-gradient - Use Shoelace to implement gradient configuration panel
  • Use Shader to implement Mesh Gradient
- Simulate random - Value Noise and Gradient Noise - Voronoi, FBM and Domain Warping - Server-side rendering in AWS lambda function
  • Export SVG
  • Use CanvasPattern to implement repeating patterns
| Linear, Radial, Conic Gradient | Mesh Gradient | | :-----------------------------------------------------------: | :--------------------------------------------: | | !Linear, Radial, Conic Gradient | !Mesh Gradient | Lesson 17 - gradient

Lesson 18 - Refactor with ECS 🔗

  • What is ECS architecture
  • Using ECS architecture to refactor the application with [Becsy]
  • Using [Spectrum] to implement UIs instead of shoelace
| Top nav and zoom toolbar | Layers and properties panels | | :-----------------------------------------------------: | :-----------------------------------------------------------: | | !Top nav and zoom toolbar | !Layers and properties panels |

Lesson 19 - History 🔗

  • Implement a simple history system, including undo and redo
Lesson 19 - history

Lesson 20 - Collaboration 🔗

  • Implement collaboration through CRDT
  • Using [Loro] / [Yjs] and BroadcastChannel to simulate collaboration on localhost
  • Using [liveblocks] to implement collaborative editing between servers and multiple clients
  • End-to-end encrypted CRDTs
  • Awareness & Presence: multiplayer cursors and comments
| BroadcastChannel with Loro or Yjs | Liveblocks as backend | | :--------------------------------------------------------------: | :----------------------------------------------------: | | !BroadcastChannel with Loro or Yjs | !Liveblocks as backend |

| Multiplayer cursors | Comments | | :--------------------------------------------------: | :---------------------------------------: | | !Multiplayer cursors | !Comments |

Lesson 21 - Transformer 🔗

  • Resize, lock aspect ratio and centered scaling
  • Rotate and change the rotation origin
  • Nudge shapes with arrow key
  • Edit Line, Polyline and Path with control points
| Rotate anchor | Resize anchor | Line transformer | Path transformer | | :------------------------------------------: | :--------------------------------------------: | :-----------------------------------------------: | :-----------------------------------------------------: | | !Rotate anchor | !Resize anchor | !Line transformer | !Path transformer |

Lesson 22 - VectorNetwork 🔗

  • Limitations of SVG Path
  • What is VectorNetwork

Lesson 23 - Mindmap 🔗

  • Analyze d3-tree, d3-flextree and @antv/hierarchy
| D3 flextree | Mindmap | | :------------------------------------------: | :------------------------------------: | | !d3-flextree | !Mindmap |

Lesson 24 - Context menu and clipboard 🔗

  • How to implement context menu with Spectrum
  • Adjust z-index with bring forward and send back
  • Writes and reads clipboard content, supports pasting serialized graphics, non-vector images, SVG and plain text
  • Drag-and-drop import of image files from file systems and pages
| Context menu | Drag'n'drop image | | :-----------------------------------------: | :--------------------------------------------: | | !context menu | !dragndrop image |

Lesson 25 - Draw rect mode and brush 🔗

  • Draw shapes mode: draw rect, ellipse, line and arrow with dragging
  • Pencil mode, including freehand
  • Brush mode
  • Eraser tool, try to support non-atomic effect
  • Laser pointer tool for presentation
| Draw rect mode | Draw arrow | Brush | | :-------------------------------------------: | :-----------------------------------------: | :------------------------------------: | | !draw rect mode | !draw arrow | !brush |

| Pencil tool | Pencil tool with freehand | Eraser | | :------------------------------------------: | :------------------------------------------: | :------------------------------------------: | | !pencil tool | !pencil tool | !eraser tool |

Lesson 26 - Selection tools 🔗

  • Single and multi-selection with Shift keypress
  • Marquee selection
  • Lasso tool
| Marquee selection | Lasso selection | | :----------------------------------------------: | :----------------------------------------------: | | !marquee selection | !lasso selection |

Lesson 27 - Snap and alignment 🔗

  • Snap to pixel grid
  • Snap to objects, display snap points & gap lines
| Snap to pixel grid | Snap to objects | | :-----------------------------------------------: | :----------------------------------------------: | | !snap to pixel grid | !snap to objects |

Lesson 28 - Integrating with AI 🔗

  • Use chatbox
  • Use Nano banana to edit image, e.g remove background
  • Use SAM with ONNX WebGPU runtime in WebWorker
  • Use Qwen-Image-Layered to decompose an image into multiple layers
  • Use UpscalerJS to upscale an image to 2X
| Use chatbox | Remove background | | :---------------------------------------------------: | :------------------------------------------------: | | !when canvas meets chat | !remove background |

| Use SAM with ONNX WebGPU runtime in WebWorker | Decompose into layers with Qwen-Image-Layered | Upscale image with UpscalerJS | | :------------------------------------------------------------: | :----------------------------------------------------------: | :--------------------------------------------: | | !use onnx and sam in webworker | !decompose image into layers | !upscale image |

Lesson 29 - Embedding HTML content 🔗

  • Create HTML container and sync camera with CSS transform
  • Paste URL, display with bookmark and iframe
  • Paste HTML content
  • Export to image
| VS Code snippet | Embeded YouTube | | :-----------------------------------------------: | :--------------------------------------------: | | !embed HTML content | !embed YouTube |

Lesson 30 - Image processing and render graph 🔗

  • Image processing, including brightness, noise, liquid glass, CRT, Glitch, Ascii etc.
  • Effects panel
  • Export time-based shader animation in GIF or WebM format
  • The design concept and implementation of Render graph
  • Use FXAA render pass
| Image processing | Effects panel | | :------------------------------------------------------------------------------------------: | :------------------------------------------: | | !image processing !effects panel | !effects panel |

| Glitch | LUTs | Raindrops | | :---------------------------------: | :-----------------------------: | :-----------------------------: | | !glitch | !luts | !rain |

Lesson 31 - Bindings between shapes 🔗

  • Analyze the data-structure design in excalidraw, tldraw, drawio and g6
  • Perimeter algorithm, including rectangle and ellipse
  • Routing rules
- Orth Connector - Segment Connector - Loop Connector
  • More line styles: rounded, curved and bezier
| Bindings | Orth Connector | Loop Connector | | :-------------------------------------: | :-------------------------------------------------: | :-------------------------------------------------: | | !bindings | !orth connector | !loop connector |

Lesson 32 - Text to diagram 🔗

  • Parse Mermaid and render in our canvas
  • Parse D2 and render in our canvas
  • Parse draw.io and render in our canvas
  • Implement label on edge and improve its readability
| Mermaid flowchart | D2 flowchart | Draw.io flowchart | | :---------------------------------------------: | :-------------------------------------------: | :---------------------------------: | | !mermaid flowchart | !d2 flowchart | !drawio |

Lesson 33 - Layout engine 🔗

  • Implement Flexbox layout with Yoga
- AlignItems & JustifyContent - Gap - Flex Basis, Grow, and Shrink - Min/Max Width and Height

| Gap | AlignItems & JustifyContent | Layout panel | | :-------------------------------------: | :--------------------------------------------: | :---------------------------------------------: | | !Yoga gap | !Yoga gap | !layout panel |

Lesson 34 - Frame and clip 🔗

  • Implement clip and mask effect with stencil buffer
  • Crop an image
  • Non-atomic eraser
| Crop an image | Brush with eraser | | :--------------------------------------------: | :-------------------------------------------------------: | | !crop image | !brush with eraser |

Lesson 35 - Tile-based rendering 🔗

  • Use vello as tile-based rendering engine via WASM, fully based on WebGPU compute shader
  • Text shaping, BiDi with parley
  • Compute bounds of Polyline, Path and hit-testing with kurbo
| Vello | | | :-------------------------------: | :-: | | !vello | |

Lesson 36 - Animation 🔗

  • How to design an animation API
  • How to implement declarative keyframes and controllers aligned with the Web Animations API
  • How to implement path, stroke, and morphing effects
  • How formats like Lottie fit in
  • Timeline panel
| Morph | Dashoffset animation | Timeline panel | | :-------------------------------: | :-------------------------------------------------------------: | :-------------------------------------: | | !morph | !animation-dashoffset | !timeline |

Lesson 37 - Global illumination with radiance cascades 🔗

  • Implement radiance cascades with WebGPU Compute Shader
- Generate SDF with analytic geometry methods - Compute cascade: ping-pong, dispatch rays, raymarch and merge - Generate mipmap - Composite with vello pipeline

| Radiance Cascades | with mermaid flowchart, mindmap, state diagram | | :-------------------------------------------: | :--------------------------------------------: | | !vello | !mermaid |

Lesson 38 - From design to code 🔗

  • Variables and themes
  • Render iconfont, import lucide, material and other iconsets
  • Use components and instances with ref and descendants
| Variables and themes | Components and instances | | :---------------------------------------: | :---------------------------------------: | | !variables | !iconfont |

| Iconfont(lucide, material and pixelarticons) | Iconfont with filters | | :------------------------------------------: | :------------------------------------------------------: | | !iconfont | !iconfont with filters |

Lesson 39 - 3D mesh rendering 🔗

  • 3D mesh pipeline
  • Linked with 2D camera, orthographic and perspective
  • 3D Gizmo
  • Picking with raycast
  • Lighting: ambient, spot, directional lights
| Gizmo | Textured earth | | :-------------------------------: | :----------------------------------------: | | !gizmo | !textured earth |

[Online App]: app.infinitecanvas.cc [infinitecanvas]: https://infinitecanvas.tools/ [Self-Deployment Guide]: /packages/app/README.md [Figma]: https://madebyevan.com/figma/building-a-professional-design-tool-on-the-web/ [Modyfi]: https://digest.browsertech.com/archive/browsertech-digest-how-modyfi-is-building-with/ [rnote]: https://github.com/flxzt/rnote [tldraw]: https://github.com/tldraw/tldraw [excalidraw]: https://github.com/excalidraw/excalidraw [rough.js]: https://github.com/rough-stuff/rough [rough-rs]: https://github.com/orhanbalci/rough-rs [zed]: https://zed.dev/blog/videogame [wgpu]: https://wgpu.rs/ [vello]: https://github.com/linebender/vello [xilem]: https://github.com/linebender/xilem [piet]: https://github.com/linebender/piet [@antv/g-device-api]: https://github.com/antvis/g-device-api [mapbox]: https://blog.mapbox.com/rendering-big-geodata-on-the-fly-with-geojson-vt-4e4d2a5dd1f2?gi=e5acafcf219d [Yjs]: https://yjs.dev/ [y-crdt]: https://github.com/y-crdt/y-crdt [Motiff]: https://www.motiff.com/blog/performance-magic-behind-motiff [My free course in Gumroad]: https://pyqiverson.gumroad.com/l/infinitecanvas?a=734684947 [Becsy]: https://lastolivegames.github.io/becsy/ [Spectrum]: https://opensource.adobe.com/spectrum-web-components [@infinite-canvas-tutorial/ecs]: https://www.npmjs.com/package/@infinite-canvas-tutorial/ecs [@infinite-canvas-tutorial/webcomponents]: https://www.npmjs.com/package/@infinite-canvas-tutorial/webcomponents [Loro]: http://loro.dev/ [liveblocks]: https://liveblocks.io/multiplayer-editing

Chat with me