{"id":18979535,"url":"https://github.com/xiaoiver/infinite-canvas-tutorial","last_synced_at":"2025-05-15T00:11:58.419Z","repository":{"id":235482729,"uuid":"775762550","full_name":"xiaoiver/infinite-canvas-tutorial","owner":"xiaoiver","description":"A tutorial on infinite canvas","archived":false,"fork":false,"pushed_at":"2025-04-11T09:06:16.000Z","size":31255,"stargazers_count":500,"open_issues_count":17,"forks_count":20,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-04-11T18:24:41.070Z","etag":null,"topics":["ecs","frontend","infinite-canvas","rendering-2d-graphics","rendering-engine","tutorial","visualization","webgl","webgpu"],"latest_commit_sha":null,"homepage":"https://infinitecanvas.cc","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/xiaoiver.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"xiaoiver","patreon":null,"open_collective":"infinite-canvas-tutorial","ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"lfx_crowdfunding":null,"polar":null,"buy_me_a_coffee":"pyqiverson0","custom":null}},"created_at":"2024-03-22T01:53:05.000Z","updated_at":"2025-04-11T09:06:20.000Z","dependencies_parsed_at":"2024-05-13T17:38:28.520Z","dependency_job_id":"f2003909-8257-4c76-85a7-0916b601196d","html_url":"https://github.com/xiaoiver/infinite-canvas-tutorial","commit_stats":null,"previous_names":["xiaoiver/infinite-canvas-tutorial"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xiaoiver%2Finfinite-canvas-tutorial","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xiaoiver%2Finfinite-canvas-tutorial/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xiaoiver%2Finfinite-canvas-tutorial/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xiaoiver%2Finfinite-canvas-tutorial/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xiaoiver","download_url":"https://codeload.github.com/xiaoiver/infinite-canvas-tutorial/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248456381,"owners_count":21106607,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ecs","frontend","infinite-canvas","rendering-2d-graphics","rendering-engine","tutorial","visualization","webgl","webgpu"],"created_at":"2024-11-08T15:44:00.788Z","updated_at":"2025-04-11T18:24:48.428Z","avatar_url":"https://github.com/xiaoiver.png","language":"TypeScript","funding_links":["https://github.com/sponsors/xiaoiver","https://opencollective.com/infinite-canvas-tutorial","https://buymeacoffee.com/pyqiverson0"],"categories":["TypeScript"],"sub_categories":[],"readme":"# An Infinite Canvas Tutorial\n\n[![Infinite canvas tutorial is released under the MIT license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/xiaoiver/infinite-canvas-tutorial/blob/master/LICENSE)\n[![PRs welcome!](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat)](https://docs.excalidraw.com/docs/introduction/contributing)\n[![Build Status](https://github.com/xiaoiver/infinite-canvas-tutorial/actions/workflows/unit-ssr.yml/badge.svg)](https://github.com/xiaoiver/infinite-canvas-tutorial/actions/workflows/unit-ssr.yml)\n[![Coverage Status](https://coveralls.io/repos/github/xiaoiver/infinite-canvas-tutorial/badge.svg?branch=master)](https://coveralls.io/github/xiaoiver/infinite-canvas-tutorial?branch=master)\n\n\u003e [My free course in Gumroad]. Feel free to rate!\n\n[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/edit/vite-tvhpcpvl?file=main.js)\n\nWhat is an Infinite Canvas? The term \"infinite\" in [infinitecanvas] is described as follows:\n\n-   High scalability. Users can freely organize content structures in a non-linear fashion.\n-   Zooming. Emulates the \"zoom in\" to get an overview and \"zoom out\" to observe details as in the real world.\n-   Direct manipulation. Provides intuitive editing capabilities for basic shapes, including moving, grouping, and modifying styles.\n\nThe [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.\n\nAs 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:\n\n-   [Figma] uses a tile-based rendering engine written in C++, compiled into WASM and then calls WebGL for rendering.\n-   [Motiff] also uses a tile-based rendering engine with WebGL.\n-   [Modyfi] uses [wgpu] from the Rust ecosystem, also compiled into WASM and then calls WebGL2 for rendering.\n-   [Zed] uses GPUI to render rectangles, shadows, text, images, and other UI elements.\n-   [Vello] and [xilem] experimentally use Compute Shader for 2D rendering.\n\nTherefore, in this tutorial, I hope to implement the following features:\n\n-   Use [@antv/g-device-api] as a hardware abstraction layer, supporting WebGL1/2 and WebGPU.\n-   Use [Becsy] to implement high-performance, highly scalable systems based on the ECS architecture.\n-   Use SDF (Signed Distance Field) rendering for circles, ellipses, rectangles, etc.\n-   GPU-accelerated text and Bezier curve rendering.\n-   Use [rough.js] to support hand-drawn styles.\n-   Use CRDT (Conflict-free Replicated Data Type) to support collaborative [Yjs].\n-   Referencing [mapbox] and [Figma], attempt to use tile-based rendering.\n\nI hope to rewrite the rendering part of the canvas with Rust in the future, but the current project completion is still relatively low:\n\n-   [wgpu] is a very reliable hardware abstraction layer, which can even implement the backend for [piet].\n-   Shaders can basically be reused.\n-   Hand-drawn styles can use [rough-rs].\n-   [y-crdt] is the Rust implementation of [Yjs].\n\nLet's get started!\n\n## Getting Started\n\nThe course project uses pnpm workspace, so you need to install pnpm first.\n\n```bash\npnpm i\n```\n\nAfter entering the course directory, run Vite devserver:\n\n```bash\ncd packages/lesson_001\npnpm run dev\n```\n\nOr you can run the site locally:\n\n```bash\npnpm run build\ncd packages/site\npnpm run dev\n```\n\nIf you want to use it in your own project, you can refer to:\n\n-   [@infinite-canvas-tutorial/ecs]\n-   [@infinite-canvas-tutorial/webcomponents]\n\n## Lesson 1 - Initialize canvas [🔗](https://infinitecanvas.cc/guide/lesson-001)\n\n-   A hardware abstraction layer based on WebGL1/2 and WebGPU.\n-   Canvas API design.\n-   Implementing a simple plugin system.\n-   Implementing a rendering plugin based on the hardware abstraction layer.\n\n## Lesson 2 - Draw a circle [🔗](https://infinitecanvas.cc/guide/lesson-002)\n\n-   Adding shapes to the canvas.\n-   Drawing a circle using SDF.\n-   Anti Aliasing.\n-   Dirty flag design pattern.\n\n## Lesson 3 - Scene graph and transform [🔗](https://infinitecanvas.cc/guide/lesson-003)\n\n-   Transformations. Make shapes support pan, zoom, rotate, and skew transformations.\n-   Scene graph.\n\n\u003cimg src=\"./screenshots/lesson3.png\" width=\"300\" alt=\"Lesson 3\"\u003e\n\n## Lesson 4 - Camera [🔗](https://infinitecanvas.cc/guide/lesson-004)\n\n-   What is a Camera?\n-   Projection transformation.\n-   Camera transformation.\n-   Camera animation. Using Landmark transition between different camera states.\n\n## Lesson 5 - Grid [🔗](https://infinitecanvas.cc/guide/lesson-005)\n\n-   Drawing straight lines using Line Geometry or screen-space techniques.\n-   Drawing dots grid.\n-   Drawing wireframe for Geometry.\n\n|                Grid                |                 Wireframe                 |\n| :--------------------------------: | :---------------------------------------: |\n| ![Grid](./screenshots/lesson5.png) | ![Wireframe](./screenshots/lesson5-2.png) |\n\n## Lesson 6 - Event system [🔗](https://infinitecanvas.cc/guide/lesson-006)\n\n-   Implement an event system compatible with DOM Event API.\n-   How to pick a circle.\n-   Implement a drag-and-drop plugin based on our event system.\n-   Support for pinch zoom gestures.\n\n## Lesson 7 - Web UI [🔗](https://infinitecanvas.cc/guide/lesson-007)\n\n-   Developing Web UI with Lit and Shoelace\n-   Implementing a canvas component\n-   Implementing a zoom toolbar component\n-   Implementing a dark theme\n\n## Lesson 8 - Optimize performance [🔗](https://infinitecanvas.cc/guide/lesson-008)\n\n-   What is a draw call\n-   Reducing draw calls with culling\n-   Reducing draw calls by combining batches\n-   Using spatial indexing to improve pickup efficiency\n\n\u003cimg src=\"./screenshots/lesson8.png\" width=\"300\" alt=\"Lesson 8\"\u003e\n\n## Lesson 9 - Draw ellipse and rectangle [🔗](https://infinitecanvas.cc/guide/lesson-009)\n\n-   How to derive the SDF representation of an ellipse or rounded rectangle\n-   Render drop-shadow and inner shadow for SDF\n-   How to determine if a point is inside an ellipse or rounded rectangle\n\n|                Drop Shadow                |                 Inner Shadow                 |\n| :---------------------------------------: | :------------------------------------------: |\n| ![Drop Shadow](./screenshots/lesson9.png) | ![Inner Shadow](./screenshots/lesson9-2.png) |\n\n## Lesson 10 - Import and export images [🔗](https://infinitecanvas.cc/guide/lesson-010)\n\n-   Exporting canvas content to PNG, JPEG and SVG formats\n-   Rendering images in the canvas\n-   Extending the capabilities of SVG, using `stroke-alignment` as an example\n\n\u003cimg src=\"./screenshots/lesson10.png\" width=\"300\" alt=\"Lesson 10 - import and export images\"\u003e\n\n## Lesson 11 - Test and server-side rendering [🔗](https://infinitecanvas.cc/guide/lesson-011)\n\n-   Jest-based test environment setup, including local and CI environments\n-   Using unit tests to improve code coverage\n-   Visual regression testing\n    -   Server-side rendering based on headless-gl, targets WebGL1\n    -   E2E testing base on Playwright, targets WebGL2 \u0026 WebGPU\n-   E2E UI testing\n-   Browser Compatibility Test based on BrowserStack\n-   Render in WebWorker\n\n## Lesson 12 - Draw polyline [🔗](https://infinitecanvas.cc/guide/lesson-012)\n\n-   Why not just use `gl.LINES`?\n-   Building Mesh in the CPU or Shader\n-   Building segments, caps and joints, antialiasing, and drawing dashed lines in shader\n-   How to calculate its bounding box?\n\n\u003cimg src=\"./screenshots/lesson12.png\" width=\"300\" alt=\"Lesson 12 - polyline\"\u003e\n\n## Lesson 13 - Draw path and hand-drawn shapes [🔗](https://infinitecanvas.cc/guide/lesson-013)\n\n-   Experimenting with SDF\n-   Trying to draw fills using some triangulating methods and strokes using polylines\n    -   Support earcut and libtess.js two triangulation schemes\n    -   Handle holes in the path correctly\n    -   Support `fillRule` property\n-   Draw some hand-drawn shapes\n\n|                Path and rough shapes                 |                 Fill rule                  |\n| :--------------------------------------------------: | :----------------------------------------: |\n| ![Path and rough shapes](./screenshots/lesson13.png) | ![Fill rule](./screenshots/lesson13-2.png) |\n\n## Lesson 14 - Canvas mode and auxiliary UI [🔗](https://infinitecanvas.cc/guide/lesson-014)\n\n-   Implement `zIndex` and `sizeAttenuation`\n-   Add more canvas modes, e.g. move and select and shapes\n\n\u003cimg src=\"./screenshots/lesson14.png\" width=\"300\" alt=\"Lesson 14 - canvas mode\"\u003e\n\n## Lesson 15 - Draw text [🔗](https://infinitecanvas.cc/guide/lesson-015)\n\n-   What's TextMetrics and how to get it in server and browser side\n-   What's shaping? Implement letterSpacing and kerning\n-   Paragraph layout\n    -   Auto wordbreak\n    -   BiDi\n    -   Handle clusters\n    -   Support text-align\n-   How to generate SDF atlas and use it to draw\n-   How to use ESDT and MSDF to improve text rendering quality\n-   How to draw bitmap font\n-   How to draw emoji\n\n|                  Render text with SDF atlas                   |     Bitmap font, emoji, BiDi and clusters      |\n| :-----------------------------------------------------------: | :--------------------------------------------: |\n| ![Linear, Radial, Conic Gradient](./screenshots/lesson15.png) | ![Mesh Gradient](./screenshots/lesson15-2.png) |\n\n## Lesson 16 - Advanced text features [🔗](https://infinitecanvas.cc/guide/lesson-016)\n\n-   Using Bezier curves to render text, shaping with OpenType and Harfbuzz\n-   Render TeX math\n-   Text decoration and dropshadow\n-   Physical text rendering\n-   Load web font with web font loader\n\n|                Shaping TeX with MathJax                 |                 Physical text                  |\n| :-----------------------------------------------------: | :--------------------------------------------: |\n| ![Shaping TeX with MathJax](./screenshots/lesson16.png) | ![physical text](./screenshots/lesson16-2.png) |\n\n## Lesson 17 - Gradient and Pattern [🔗](https://infinitecanvas.cc/guide/lesson-017)\n\n-   Use CanvasGradient to implement gradients\n    -   Imperative. Create textures using the Device API\n    -   Declarative. Supports CSS gradient syntax: `linear-gradient`, `radial-gradient`, `conic-gradient`\n    -   Use Shoelace to implement gradient configuration panel\n-   Use Shader to implement Mesh Gradient\n    -   Simulate random\n    -   Value Noise and Gradient Noise\n    -   Voronoi, FBM and Domain Warping\n-   Export SVG\n-   Use CanvasPattern to implement repeating patterns\n\n|                Linear, Radial, Conic Gradient                 |                 Mesh Gradient                  |\n| :-----------------------------------------------------------: | :--------------------------------------------: |\n| ![Linear, Radial, Conic Gradient](./screenshots/lesson17.png) | ![Mesh Gradient](./screenshots/lesson17-3.png) |\n\n\u003cimg src=\"./screenshots/lesson17-2.png\" width=\"300\" alt=\"Lesson 17 - gradient\"\u003e\n\n## Lesson 18 - Refactor with ECS [🔗](https://infinitecanvas.cc/guide/lesson-018)\n\n-   What is ECS architecture\n-   Using ECS architecture to refactor the application with [Becsy]\n-   Using [Spectrum] to implement UIs instead of shoelace\n\n\u003cimg src=\"./screenshots/lesson18.png\" width=\"300\" alt=\"Lesson 18 - ecs\"\u003e\n\n## Lesson 19 - History and Collaboration [🔗](https://infinitecanvas.cc/guide/lesson-019)\n\n-   Implement a simple history system, including undo and redo\n-   Implement collaboration through CRDT\n\n[infinitecanvas]: https://infinitecanvas.tools/\n[Figma]: https://madebyevan.com/figma/building-a-professional-design-tool-on-the-web/\n[Modyfi]: https://digest.browsertech.com/archive/browsertech-digest-how-modyfi-is-building-with/\n[rnote]: https://github.com/flxzt/rnote\n[tldraw]: https://github.com/tldraw/tldraw\n[excalidraw]: https://github.com/excalidraw/excalidraw\n[rough.js]: https://github.com/rough-stuff/rough\n[rough-rs]: https://github.com/orhanbalci/rough-rs\n[zed]: https://zed.dev/blog/videogame\n[wgpu]: https://wgpu.rs/\n[vello]: https://github.com/linebender/vello\n[xilem]: https://github.com/linebender/xilem\n[piet]: https://github.com/linebender/piet\n[@antv/g-device-api]: https://github.com/antvis/g-device-api\n[mapbox]: https://blog.mapbox.com/rendering-big-geodata-on-the-fly-with-geojson-vt-4e4d2a5dd1f2?gi=e5acafcf219d\n[Yjs]: https://yjs.dev/\n[y-crdt]: https://github.com/y-crdt/y-crdt\n[Motiff]: https://www.motiff.com/blog/performance-magic-behind-motiff\n[My free course in Gumroad]: https://pyqiverson.gumroad.com/l/infinitecanvas?a=734684947\n[Becsy]: https://lastolivegames.github.io/becsy/\n[Spectrum]: https://opensource.adobe.com/spectrum-web-components\n[@infinite-canvas-tutorial/ecs]: https://www.npmjs.com/package/@infinite-canvas-tutorial/ecs\n[@infinite-canvas-tutorial/webcomponents]: https://www.npmjs.com/package/@infinite-canvas-tutorial/webcomponents\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxiaoiver%2Finfinite-canvas-tutorial","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxiaoiver%2Finfinite-canvas-tutorial","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxiaoiver%2Finfinite-canvas-tutorial/lists"}