mesh-avatar-studio
Turn one illustration into an animated 2D mesh avatar with a coding agent and a local editor
Convert scanned film negatives to positives in your browser. Free, no uploads, supports RAW (NEF, CR2, ARW, DNG, RW2) plus 16-bit PNG and iPhone ProRaw, with Kodak/Fuji/Ilford film presets.
DesignJavaScript · HTMLGitHub @lexluthor0304OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗Negative Converter helps film photographers turn scanned or camera-digitized film into clean, natural-looking positives with a workflow made for everyday editing. It brings frame cleanup, film-aware conversion, roll consistency, finishing controls, and export into one focused workspace.
Whether you are reviewing a fresh roll, restoring family negatives, or preparing a consistent set for sharing, the app is designed to make film conversion feel fast, approachable, and dependable while keeping your photos private.
HEIC import, automatic same-stock roll analysis, local semantic colour anchors, learned defaults, review filtering and consistent colour-profile export are available in the local workflow. CMYD remains a primary control above basic adjustments; Advanced reveals the optional tools. Desktop folder watching is optional. See the automation design, workflow guide and validation and remaining acceptance.
docs/film-edge-reader.md)docs/roll-analysis.md)docs/light-table.md)docs/darkroom.md)docs/camera-scanning.md)docs/roll-home.md)docs/technical-depth.md)docs/expired-film-rescue.md)negative2positive/src/silvercore/engine/FilmPresets.js (supports alias fallback for older preset IDs)UPNG.js to decode 16-bit PNGslibraw-wasm npm package to support .cr2, .nef, .arw, .dng, .raw, .rw2 formatsFilmPresets.js and is grouped dynamically by film type in the UI@neoanaloglabkk/lensfun-wasm with npm local assets first + CDN fallback@techstark/opencv-js loaded dynamically from the npm package asset URLrequestAnimationFrameFilm Negative → Positive Converter
This repo includes a Tauri wrapper to package the web app as an offline desktop application for Windows / macOS / Linux.
npm ci
npm run dev:web
This app must be deployed from the Vite build output, not by serving source files directly.
Required settings (as recorded in .vercel/project.json):
negative2positivenpm cinpm run build:webnegative2positive/distnpm run build:web generates negative2positive/dist (for local/Tauri) and also syncs it to root dist.
Because the Root Directory is negative2positive, Vercel reads negative2positive/vercel.json (cache and security headers); the repository-root vercel.json is not applied to the deployment.
If Vercel serves negative2positive/index.html directly, module imports like pako / utif / jszip will not resolve in browser and upload buttons can stop working.
The header Feedback button posts to negative2positive/api/feedback.mjs (a Vercel serverless function) at /api/feedback/; the desktop app uses https://negative-converter.tokugai.com/api/feedback/. Keep the trailing slash: with trailingSlash: true Vercel answers /api/feedback with a 308, and the desktop app's cross-origin preflight fails on a redirect. The Vercel project's Root Directory is negative2positive, so functions MUST live under negative2positive/api/ — an api/ directory at the repository root is silently ignored. The function files the message as a GitHub issue labeled feedback. Configure in Vercel → Project → Settings → Environment Variables:
FEEDBACK_GITHUB_TOKEN (required): fine-grained personal access token with Issues: Read and write and Contents: Read and write on the target repo. Contents is what lets attached screenshots be uploaded to the orphan feedback-assets branch, which must already exist — without it the issue is still filed but the images are silently dropped. Without the token entirely the endpoint returns 503 and the form shows the error state.FEEDBACK_GITHUB_REPO (optional): owner/repo to file issues in; defaults to lexluthor0304/NegativeConverter.The endpoint allows cross-origin calls only from the production domain, Tauri desktop webviews, and localhost dev servers.
npm run tauri:dev
npm run tauri:build
Build outputs are placed under:
src-tauri/target/release/bundle/If macOS shows "Negative Converter is damaged and can't be opened", this is because the app is not yet notarized by Apple. Use one of these methods:
Method 1 — Terminal command (recommended):
xattr -cr /Applications/Negative\ Converter.app
Method 2 — Right-click open: Right-click (or Control-click) the app → select Open → click Open in the confirmation dialog.
Method 3 — System Settings: Go to System Settings → Privacy & Security, scroll down and click Open Anyway next to the blocked app message.
sudo.gvfs/GLib ABI mismatches*_legacy-glibc235.AppImage) defaults DMABUF off for startup stabilityNEGATIVE_CONVERTER_DMABUF=on ./Negative\ Converter*.AppImageNEGATIVE_CONVERTER_DMABUF=off ./Negative\ Converter*.AppImageNEGATIVE_CONVERTER_DMABUF=shm ./Negative\ Converter*.AppImage
sets WEBKIT_DMABUF_RENDERER_FORCE_SHM=1. WebKit keeps accelerated compositing and copies each
frame through shared memory, instead of painting the whole page in software as off does.
It is not yet the legacy default: that needs the startup matrix below.WEBKIT_DISABLE_DMABUF_RENDERER or WEBKIT_DMABUF_RENDERER_FORCE_SHM you set yourself always wins.[linux-compat] lines) and, once a photo is open, one [webview]
line with the WebGL renderer, the decision, the WebKitGTK version and the starting preview tier.
Include both in bug reports about sluggish sliders.[memory] line at startup shows the WebKitGTK memory limit the app applies before the window
opens (half of RAM, so WebKit's 30 s cache and JIT purge starts at a quarter of RAM instead of
1.5 GiB), or WebKit defaults when /proc/meminfo could not be read.off, no render node) or WebGL is a
software rasteriser (llvmpipe, SwiftShader), slider and curve drags preview at up to 1 MP and the
full display resolution returns on release. Other hosts drop to that tier only when frames run slow.
NEGATIVE_CONVERTER_FRAME_LOG=1 logs every drag's frame intervals and canvas size as [webview] lines.shm the legacy default (Ubuntu 22.04 and Debian 12; Intel, AMD, NVIDIA
proprietary; X11 and Wayland; each must start without a blank window or crash and create a WebGL
context): not run yet.*_legacy-glibc235.AppImage).src-tauri/tauri.conf.jsonsrc-tauri/Cargo.tomlmainvX.Y.Z tagnegative-converter/release/vX.Y.Z/If you want the release workflow to upload installers to R2, add one of these GitHub Actions secret sets:
Option A: R2 S3 API token
R2_ACCESS_KEY_IDR2_SECRET_ACCESS_KEYR2_BUCKETR2_ENDPOINT (e.g. https://<accountid>.r2.cloudflarestorage.com/)Option B: Cloudflare API token (no S3 keys)
CLOUDFLARE_API_TOKENCLOUDFLARE_ACCOUNT_IDR2_BUCKETFeel free to fork, open issues, or submit pull requests with ideas or improvements.
This tool is designed to be simple, fast, and modifiable.
MIT License
Special thanks to LibRaw-Wasm by ybouane,
which made it possible to support various raw image formats such as .cr2, .nef, .arw, .dng, .raw, and .rw2 directly in the browser via WebAssembly.
Your work was an essential reference and greatly accelerated development.
HEIC/HEIF decoding uses the browser when supported, then the bundled libheif WASM decoder in a worker. See negative2positive/public/codecs/README.md for licence and replacement instructions.
The desktop app can decode RAW files with LibRaw 0.22.1 linked natively (used under CDDL-1.0; source in src-tauri/vendor/libraw, notices in negative2positive/public/licenses/raw-decoder-notices.txt). See docs/native-raw-decode.md.