Library / SDK
LHRUN/paint-board avatar
LHRUN/paint-board

paint-board: a browser canvas that mixes Fabric.js with hand-drawn brushes

🎨 A powerful multi-end drawing board that brings together a lot of creative brushes to experience a whole new range of drawing effects!

2,641 stars299 forksTypeScriptMIT

At a glance

What is it?
paint-board is an open source web drawing board built on Fabric.js, where 15 brush styles and vector shapes coexist with selection, grouping, layers and multi-file boards. The interesting engineering is in how per-stroke graphics are kept separate from editable objects.
Who is it for?
paint-board fits a developer who wants a self hosted drawing surface for a project, a workshop or an internal tool, and it does not fit a team that needs real time multi user editing, offline use or a mobile drawing workflow, since the canvas is a client side application with per board state in the browser.
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

Two drawing models share one canvas

paint-board is an open source web based creative canvas, and its defining feature list mixes two drawing models that most tools keep separate. There is a free drawing mode with 15 brush styles, and there is a shape drawing mode that places built-in shapes and Lucide icon graphics with a click.

The brush list is unusually long for a canvas tool. The README names basic, pen, outlined pen, rainbow, multi-shape, pixels, multi-color, text, multi-line, mesh, multi-point, wave, thorn and ink. Those are not variations on line width. Some of them look like textured strokes, some like a marker, and some like procedural scribbles, which is the point of the project as the author describes it, a range of drawing effects rather than one faithful pencil.

Shape drawing sits on top of that with configurable line width, line style, fill color and fill style, and the Lucide icon library is available as a source of clickable graphics. A stroke from the pen and a filled icon therefore end up as different kinds of object on the same surface, and how the application reconciles those two after the fact is where the real design work sits.

The dependency list explains the architecture better than the README does

The package manifest is where the mechanism becomes visible. The canvas itself is Fabric.js at version 5.3.0, and the brush set is driven by perfect-freehand, a library that generates stroke outlines from raw pointer input rather than drawing a line directly.

That pairing explains a lot. perfect-freehand turns a drag into a filled outline shape with pressure-sensitive width, which is why the brush styles can produce mesh, wave, thorn and multi-line effects. Fabric.js is an object model rather than a pixel buffer, so every stroke becomes an object on the canvas with its own properties, and the selection mode can drag, scale, rotate and recolor those objects afterwards.

The rest of the manifest is a fairly standard React application. State lives in zustand, immutable updates go through immer, ids come from uuid, sorting for the layer list uses sortablejs, and i18next with react-i18next provides the five interface languages. Two dependencies stand out for a drawing app: roughjs, which is a second rendering path for sketchy hand-drawn shapes, and react-best-gradient-color-picker for the colour UI, with gradient-parser reading gradient values.

The remaining pieces are less visible but meaningful. jsondiffpatch suggests the board state is diffed rather than rewritten, idb-keyval gives access to IndexedDB, and @huggingface/transformers is listed as a dependency, which lines up with the author's write-up on combining the canvas with Transformers.js for image processing.

Selection, grouping and the layer list are what separate it from a paint app

Eraser mode erases linearly with configurable width, but the more consequential mode is selection. You can select a region by clicking or by dragging a box, and after that selection you can move it, scale it, rotate it, change its colours, and group multiple objects together.

Grouping and the layer list are the two features that push this past a drawing toy. The action bar lists undo, redo, copy, group, delete, draw text, upload image, clear drawing content, save as image, open the layer list and open the file list. That is an object-oriented editor vocabulary, not a sketchpad vocabulary, and it is what you need if the output is going to be a diagram, a mockup or a set of composed graphics rather than a single illustration.

The canvas mode adds the surrounding controls: canvas background, custom size and global drawing configuration. The multi file configuration feature lets you switch between multiple canvases, give each a custom title, and add, rename, delete, upload and batch download boards.

What is missing is equally informative. The README describes no text formatting options, no alignment controls, no clipping masks and no layer opacity, so treat the feature list as the ceiling rather than the floor of what a future release might include.

Running it locally with pnpm, or as a static bundle behind nginx

The quick start path uses pnpm, and the manifest pins the package manager to pnpm 8.15.9 with a pnpm-lock.yaml in the repository, so an install without the lock file is a different build from the one the author ships:

bash
git clone https://github.com/LHRUN/paint-board.git
cd paint-board
pnpm install
pnpm dev

The scripts section defines the rest of the workflow: vite for dev and build, vitest run for tests, tsc --noEmit for typecheck, eslint for scripts and stylelint for styles. The engines field requires Node 22 or newer and pnpm 8.15.9 up to but not including 9.

The Docker path builds the same application and serves it as static files. The image is a two stage build: a node 22 alpine stage that installs with a frozen lockfile and runs the production build, then an nginx alpine stage that copies the built output and the nginx.conf from the repository root:

sh
docker build -t paint-board .
sh
docker run -d -p 8080:80 --name paint-board paint-board

The container exposes port 80 and the published path is under a subpath, so the README's access instruction is not the bare root:

code
http://localhost:8080/paint-board/

That subpath detail comes from nginx.conf and from the Dockerfile copying the build into /usr/share/nginx/html/dist, so a self hosted deployment behind a reverse proxy has to preserve the same path prefix.

The 3.0.0 rewrite is the upgrade story to plan around

The release history has three entries and the gaps are large. Version 1.5.7 shipped on 2025-06-12, version 2.0.0 on 2025-10-13, and version 3.0.0 on 2026-10-18. A major version every year, with the latest landing on the same day as the last push recorded for the repository.

The author documents the reason. The README's document section separates writing into the period before and after a refactoring with Fabric.js, and the earlier articles are titled around selection, dragging and scaling on canvas, while the later ones cover creative brushes, time reversal in the canvas, and combining the canvas with Transformers.js. So the 2.x and 3.x lines represent a rewrite of the drawing internals onto Fabric.js objects, and the brushes were largely rebuilt on top of that foundation.

The consequence for an adopter is that board data written by an older version may not be readable by a newer one, and the README does not document a migration path, a file format version or a rollback procedure. The CHANGELOG.md in the repository root is the place to look for what actually changed between majors before you switch.

The alternative to upgrading is to pin to a specific tag, which is straightforward since the published board is static files behind nginx and nothing in the architecture is version coupled to a server.

Where it is the wrong tool

paint-board is a client side single user application, and several common requirements are simply absent from the design.

There is no server component in the repository, no Dockerfile that runs a database, and no mention of authentication, accounts or a shared store. A board lives in the browser and is exported to an image or managed through the file list, which means two people cannot edit the same board at the same time. The dependency on idb-keyval suggests local browser storage is used, and the README does not describe any sync or collaboration path, so treating it as a collaborative whiteboard would be a mistake.

Other gaps follow from the same shape. There is no offline story described in the README, no tablet or touch specific guidance even though the canvas drawing model is well suited to stylus input, and no mention of file format guarantees for the board list, so a board saved in one browser profile is not obviously portable to another.

There is also a licensing point worth stating plainly. The project itself is MIT, which is permissive and lets you embed it in a commercial product with the notice retained. The dependencies carry their own licences, and each carries its own terms that a redistributor has to honour separately from the MIT grant of this repository.

Against a plain raster paint app, the trade is fidelity over speed

The usual alternative to a project like this is a general drawing app built on a raster canvas, where a stroke is a path of pixels and a shape is a filled region. That approach is faster to implement, lighter in the browser, and easier to reason about when the output is a picture rather than a document.

paint-board takes the other route, and the difference shows up in what you can do afterwards. Because Fabric.js keeps strokes and shapes as objects with selectable properties, you can select a drawn region and recolor it, rotate a shape, group elements, reorder them in a layer list, and export the composition as an image. A raster paint app gives you none of that, because a painted stroke has no identity to select.

The brush effects go further. A textured stroke generated by perfect-freehand is an outline filled with a computed path, so it can carry a fill colour and a line style, which is why the shape mode offers fill color and fill style options at all. The trade is cost: every stroke is a heavier object, and roughjs plus Fabric.js plus the transformers dependency all ship to the client.

So the choice is between a fast pixel surface for quick illustration and an object model for composed, editable graphics. A diagram, an icon composition or a UI mockup lands in the second category, and that is the case this project serves.

Editorial conclusion

paint-board fits a developer who wants a self hosted drawing surface for a project, a workshop or an internal tool, and it does not fit a team that needs real time multi user editing, offline use or a mobile drawing workflow, since the canvas is a client side application with per board state in the browser. Verify three things first: that the brush and shape behaviour suits your use rather than a generic raster brush, that the version 3.0.0 build works with your Node 22 setup since the major release landed on 2026-10-18, and that a board saved through the file list is recoverable, because the README does not describe where board data is stored. The project is MIT licensed and the last push was on 2026-10-18.

Frequently asked questions

How do I run paint-board locally for development?

Clone the repository, run pnpm install and then pnpm dev, which starts the Vite dev server. The manifest requires Node 22 or newer and pins the package manager to pnpm 8.15.9.

How do I serve paint-board with Docker?

Build the image with docker build -t paint-board . and run it with docker run -d -p 8080:80 --name paint-board paint-board. The container serves the built static files through nginx and the application is reachable at http://localhost:8080/paint-board/.

What brushes and shapes does paint-board offer?

Free drawing provides 15 brush styles including pen, outlined pen, rainbow, pixels, multi-color, mesh, wave, thorn and ink. Shape drawing offers built-in shapes plus graphics from the Lucide icon library, with configurable line width, line style, fill color and fill style.

Can multiple people edit the same board in paint-board?

The project does not describe any server, account or sync mechanism, and the repository contains only a Dockerfile that builds static files behind nginx. Boards are managed locally through the file list, so treat it as a single user tool.

Which browsers does paint-board support?

The README recommends the latest Google Chrome and lists minimum versions of Edge 80+, Firefox 80+, Chrome 70+ and Safari 16.4+.

Official sources

  1. LHRUN/paint-board 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/lhrun-paint-board.svg)](https://hysenlabs.com/projects/lhrun-paint-board)

Was this useful?