A local-first browser editor for ASCII, dithering, halftone, glitch, and retro visual effects.
Everything runs in the browser — no uploads, no accounts. Drop an image, a video, or a GLB model, pick an effect, and export the result as PNG, JPG, SVG, or plain text.
- 22 effects across ASCII, pixel sorting, dithering, halftone, CMYK, VHS glitch, scanlines, grain, noise, edge detection, and more.
- Four input sources: still images, video files, webcam capture, and 3D models.
- 3D model preview: drag a self-contained
.glb(or embedded.gltf) into the editor and orbit it with the mouse; the rendered scene flows through the same effect pipeline. - Configurable preview: processed view, split (before/after), and raw source comparisons.
- Effect-aware controls: sliders, palettes, dither algorithms, and sort comparators only appear for the effect you have selected.
- Theme-aware UI: monochrome, VT320 amber, cassette green, and cold war radar looks.
- Installable PWA: install offline from any modern browser.
- Responsive shell: side panels collapse into drawers on narrow viewports.
- No telemetry. Effect processing stays on your device. The UI font is bundled; no remote font CDN.
npm install
npm run devThen open the printed URL (usually http://localhost:5173) and drop in an image.
| Command | What it does |
|---|---|
npm run dev |
Start the Vite development server with hot module reload. |
npm run build |
Type-check with tsc and produce a production build in dist/. |
npm run preview |
Serve the production build locally to verify the PWA output. |
npm run lint |
Lint the source with ESLint and fail on any warnings. |
npm run test |
Run unit tests with Vitest. |
npm run check |
Lint, test, then build — the same gate the CI workflow runs. |
- React 18 + TypeScript with
strictmode enabled across the project. - Vite 6 for the dev server, bundling, and PWA generation.
- Tailwind CSS 3 with custom CSS variables for theming.
- Three.js for the interactive 3D model viewer.
- vite-plugin-pwa / Workbox for offline support and the installable shell.
- Vitest for unit tests on pure utilities.
.
├── index.html # HTML entry, OG metadata, manifest links
├── public/ # Icons, PWA assets, social preview image
├── src/
│ ├── App.tsx # ThemeProvider + Editor root
│ ├── main.tsx # React DOM bootstrap + bundled font
│ ├── index.css # Tailwind layers + terminal design tokens
│ ├── components/ # Editor surface, canvas, sidebar, 3D viewer
│ ├── contexts/ # Theme context + provider
│ ├── hooks/ # Reusable React hooks (theme)
│ ├── types/ # Effect types, presets, palette definitions
│ └── utils/
│ ├── canvasRenderer.ts # The 2D Canvas2D effect engine
│ ├── exportArt.ts # Source-sampled text / SVG export
│ ├── sourceType.ts # File MIME + extension detection
│ └── motion.ts # prefers-reduced-motion helper
├── tailwind.config.ts
├── postcss.config.js
├── vite.config.ts
├── vitest.config.ts
├── tsconfig*.json
└── eslint.config.js
Canvas and ModelViewer both share the CanvasEffectRenderer in
src/utils/canvasRenderer.ts. The renderer:
- Down-samples the source into a small "sample" canvas (one cell = one pixel).
- Computes luma, red/green/blue, and applies brightness / contrast.
- Dispatches to a per-effect drawing routine (ASCII, Bayer/Floyd–Steinberg, halftone, CMYK, etc.).
- Composites the processed result back onto the output canvas, honoring the
fxOpacity,blur,backgroundOpacity, scanline, and noise-overlay toggles.
For 3D models, Three.js renders into a WebGL canvas that the same renderer reads as its input source — so every 2D effect also applies to 3D content.
| Format | Behavior |
|---|---|
| PNG / JPG | Raster snapshot of the processed preview canvas. |
| Text | Plain ASCII sampled from the source (cell grid, charset, brightness/contrast/invert). An all-space result is still a valid download. |
| SVG | Vector character grid sampled from the source (same path as text export). If the source cannot be sampled, falls back to a raster PNG embedded in an SVG wrapper — not a path-trace of the pixel preview. |
| Category | Effects |
|---|---|
| ASCII & text | ascii, dithering |
| Pixel | pixel-sorting, pixelate, pixel-grid, mosaic |
| Matrix | matrix-rain, matrix-dots |
| Halftone | halftone (+ CMYK toggle), halftone-cmyk, halftone-dots, crosses |
| VHS / analog | vhs-glitch, scanlines |
| Edge detect | edge-detection, edge-lines, convolution |
| Other | threshold, invert, led, grain, noise |
- UI transitions honor
prefers-reduced-motion. - Continuously animated effects (
matrix-rain,vhs-glitch,grain,noise) freeze to a single frame when reduced motion is preferred. Video and webcam still play because they are user-controlled media. - Controls use labels,
aria-*attributes, and focus-visible styles. - Themes persist via
localStorage(graceful fallback when storage is locked).
| Feature | Minimum version |
|---|---|
| Vite/React | Any browser released in the last two years |
| Canvas 2D | All modern browsers (primary effect pipeline) |
| WebGL | Chrome 56+, Firefox 51+, Safari 15+, Edge 79+ (3D viewer) |
Issues and pull requests are welcome at
nkcbuilds/ASCIIconverter.
Please run npm run check before submitting.
MIT — see LICENSE.