# Pindo: fuse bead charts generated entirely inside the browser

> Pindo converts photos, illustrations and pixel art into numbered fuse bead patterns, and it also reads unlabelled charts you already have. Eight brand palettes decide the colour codes and quantities, everything runs on Canvas in your own browser, and the shipped artefact is a static export in out/.

**LunarXuan/Pindo** — Local-first fuse bead pattern generator｜将图片与像素画转换为带编号、色号和用量统计的拼豆图纸

- Repository: https://github.com/LunarXuan/Pindo
- Website: https://pindo-eight.vercel.app
- Stars: 418 · Forks: 17
- Language: TypeScript
- License: GPL-3.0
- Published: 2026-09-17 · Updated: 2026-09-17 · Language: en
- Canonical page: https://hysenlabs.com/projects/lunarxuan-pindo

## Nothing leaves the browser, because there is no image-upload API

The privacy claim is architectural rather than a promise. Uploaded images are decoded and processed with browser Canvas APIs and typed arrays, and the application has no image-upload API and no application backend at all. Every feature in the list runs on the machine you opened it on: converting a photo into a chart, highlighting one bead colour inside a finished chart, and exporting a PNG with its grid and usage legend. One caveat is volunteered, and it concerns hosting rather than privacy, since when you self-host whichever static host you choose still serves the application assets as usual. That is the whole extent of the network surface. For a tool whose input is a personal photograph, this is the property that decides whether it is usable at all.

## Eight palettes and a palette limit decide what the chart looks like

Pindo ships eight brand palettes: MARD, Hama, Perler, Artkal S, COCO, Manman, Panpan and Mixiaowo. Which one you choose determines every colour code and every bead count in the usage table, so the palette is not a cosmetic setting. Three controls then reshape the result: a creative mode, a dithering strategy and a palette limit, sitting alongside image adjustments, with filters that include or exclude whole colour series and individual colour codes by hand. Sizing is expressed in beads rather than pixels, with width and height set separately and an optional aspect-ratio lock. Pattern recognition works in the opposite direction, taking an existing unlabelled chart with or without a visible grid and adding the grid, colour codes and usage information.

## Starting on Windows means double-clicking 启动 Pindo.cmd

The low-friction entry point is a file sitting in the repository root. Clone or download, then double-click `启动 Pindo.cmd`. On the first launch the script installs locked dependencies, builds the static application and opens the local server, and later launches skip the first two steps. That documented launcher is Windows-specific, and the repository also carries `Start-Pindo.ps1`, which the README does not mention. For anyone who wants a terminal instead, three commands and a browser tab are the whole setup:

```powershell
npm ci
npm run dev
```

The development server answers on http://localhost:3000. `npm ci` rather than `npm install` is the deliberate choice, and it lines up with the `package-lock.json` committed at the root.

## npm test is Vitest, and test:bench never reaches the command table

Six scripts appear in the command table. `npm run dev` starts the Next.js development server, `npm run build` produces the static export in `out/`, `npm test` runs the Vitest suite, `npm run lint` runs ESLint, and `npm run android:sync` and `npm run android:open` deal with the Android project. package.json holds more than that. `android:add` maps to `cap add android` and shows up in the Android section rather than the table. `start` maps to `next start` and is not documented anywhere. `test:bench` maps to `vitest bench` with no mention at all. Vitest 4 is the runner and `@vitest/ui` sits in devDependencies, so a benchmark harness exists in the repository even though nobody is pointed at it.

## The static export lands in out/ and server.mjs binds 127.0.0.1

A production-like local path exists so you can look at the shipped artefact without deploying it:

```powershell
npm ci
npm run build
node server.mjs --open
```

The export is generated in `out/`, and `server.mjs` serves it only on `127.0.0.1` by default, so nothing is exposed to the network unless you change that yourself. That is the boundary worth knowing: the browser-only privacy model covers image processing, not the bind address of the local server. The same `out/` directory is what Capacitor packages, which is why the static build is not an optional step before an Android sync. Worth noting for anyone hosting this: `next build` is what produces the export under a Next.js 16 static export configuration, and `next start` is not the command that serves it. A published copy at a static host is therefore a folder upload, with PWA assets and a service worker alongside it from `public/`.

## Capacitor's app id is com.pindo.app and the signing key stays out of git

The Android project is generated rather than committed. Capacitor 8 configuration ships with application ID `com.pindo.app` and web directory `out`, and the project directory is created locally the first time you need it.

```powershell
npm run android:add
npm run android:sync
npm run android:open
```

`android:sync` runs the static build before `cap sync android`, so a stale bundle cannot be packaged by accident, and `android:open` hands the result to Android Studio. ANDROID.md holds the detail. One warning deserves repeating: Android signing keys such as `pindo-release.jks` are sensitive and must not be committed. Native bridge definitions live in `lib/native/`, and `@capacitor/filesystem` and `@capacitor/share` are in the dependency list, which is what gives the packaged app its file and sharing paths.

## Behaviour changes need tests, and dependency changes are security-sensitive

The contributing guidance is short and has teeth. Issues and pull requests are welcome. For behaviour changes, include or update the relevant tests and verify the static build, which is the reason Vitest sits next to the engine code and why the algorithm work in `lib/engine/` is the part worth covering first. Changes to dependencies, launch scripts, local file serving and native bridges are treated as security-sensitive, a defensible line in a project whose launcher installs packages and whose local server opens a socket. The layout follows the same logic: `lib/engine/` holds scaling, matching, cleanup and chart recognition, `lib/export/` holds the PNG and PDF modules, `lib/data/palettes/` holds the compiled brand data, and `lib/i18n/` holds the Chinese and English strings. Pindo is GPL-3.0 only, Copyright 2026 LunarXuan, the package is private at version 0.1.0, no GitHub releases exist, and the last push landed on 2026-09-21.

## Conclusion

Adopt Pindo when you already own bead stock in one of the eight supported brands and want the chart to stay on your own machine. Skip it when patterns have to be shared between people, since there is no backend and no account model to hold them. Two things to verify before committing an afternoon: whether your brand's colours are already compiled under `lib/data/palettes/`, and whether a static export in `out/` fits your hosting, given that the documented local path is `node server.mjs --open` on 127.0.0.1 and nothing wider.

## FAQ

### Does Pindo upload my images to a server?

No. Images are decoded and processed with browser Canvas APIs and typed arrays, and the application has no image-upload API and no application backend. A self-hosted copy still fetches its application assets from whichever static host serves it.

### Which fuse bead brands does Pindo support?

Eight palettes ship with it: MARD, Hama, Perler, Artkal S, COCO, Manman, Panpan and Mixiaowo. You can also include or exclude whole colour series and individual colour codes, and cap how many colours a single conversion is allowed to use.

### How do I run Pindo on Windows without a terminal?

Clone or download the repository and double-click 启动 Pindo.cmd. On the first launch the script installs locked dependencies, builds the static application and opens the local server for you.

### How do I build the Android app from Pindo?

Run npm run android:add to generate the Android project locally on first use, then npm run android:sync and npm run android:open. Capacitor is configured with application ID com.pindo.app and web directory out, and ANDROID.md covers the rest.

### Can Pindo read a bead chart that is already drawn?

Yes. Pattern recognition takes unlabelled charts with or without a visible grid and adds grid lines, colour codes and usage information. Colour-code highlighting then lets you inspect and highlight individual bead colours inside the finished chart.

## Sources

- [Issues](https://github.com/LunarXuan/Pindo/issues)
- [License: GPL-3.0](https://github.com/LunarXuan/Pindo/blob/main/LICENSE)
- [LunarXuan/Pindo on GitHub](https://github.com/LunarXuan/Pindo)
- [Project website](https://pindo-eight.vercel.app)
- [README](https://github.com/LunarXuan/Pindo/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/lunarxuan-pindo
