# Pic Lite > Compress JPEG, PNG, WebP and animated GIF files entirely inside the browser tab. A > continuous 1–100 quality slider for the lossy formats; median-cut palette quantisation > with Floyd–Steinberg dithering for PNG, because the canvas PNG writer ignores quality > and the palette is the only real size lever; a hand-written LZW reader and writer for > GIF, because a canvas cannot write GIF at all; resizing, metadata stripping, watermarks, > batch downloads and CSV/JSON/Markdown reports. Nothing is uploaded — there is no server > in the image path, no account is required and no credits are spent. An independent web > port of PicLite by amiaoapp (https://github.com/amiaoapp/PicLite, GPL-3.0), and itself > GPL-3.0. Pic Lite is the tool you open when you have a folder of screenshots that are too big to attach, a photograph that needs to fit an upload limit, or a GIF that has to drop under a few hundred kilobytes — and you would rather not hand any of them to a website. Everything runs in the tab. Close it and there is no copy anywhere. ## Attribution and licence This is a **derived work** of [PicLite by amiaoapp](https://github.com/amiaoapp/PicLite), licensed GPL-3.0. The feature set, the compression strategy and the anti-enlargement rule come from that project. **This is not the official PicLite release** — it is not published by, affiliated with or endorsed by the PicLite authors, and none of their code, branding or assets are redistributed here. Every line of this application was written from scratch for a static browser bundle. As a derived work Pic Lite is itself GPL-3.0: the full licence text is served at `/LICENSE.txt`, and the corresponding source is the unminified, unbundled JavaScript this page loads (`/compress.js`, `/quantize.js`, `/pngenc.js`, `/gifcodec.js`, `/metastrip.js`, `/watermark.js`, `/encoder.js`, `/worker.js`, `/pipeline.js`, `/zipenc.js`, `/download.js`, `/report.js`, `/history.js`, `/examples.js`, `/app.js`). ## How each format is actually made smaller This is the part worth quoting, because most in-browser compressors are vaguer about it than they should be. **JPEG and WebP.** The browser's own encoder, behind `canvas.toBlob`, driven by a continuous 1–100 quality. Every attempt re-encodes from the *original decode*, never from the previous attempt, so nudging the slider repeatedly does not stack generation loss. **PNG.** `canvas.toBlob("image/png")` always emits 8-bit truecolour and ignores the quality argument entirely — a PNG quality slider built on top of it does nothing measurable. So PNG quality here means the **palette**: median-cut quantisation to 2–256 colours, optional Floyd–Steinberg error diffusion, and the app's own PNG writer with sub-byte index packing (1, 2, 4 or 8 bits per pixel), adaptive per-scanline filter selection, and a `tRNS` chunk so transparency survives. Both a quantised and a truecolour candidate are written and the smaller file wins, which matters because a two-tone logo sometimes deflates better as truecolour than as an index stream. **GIF.** There is no GIF encoder behind `toBlob`, and `createImageBitmap` over an animated GIF returns one still frame. So Pic Lite carries its own codec: LZW decode and encode, correct handling of all four disposal methods, frame composition, per-frame palettes, and inter-frame differencing that sends only the rectangle which changed. Frame delays and loop count are preserved. Verified round-trip on a 24-frame animation: 53,521 bytes in, 12,855 bytes out, and the decoded frames compare pixel-identical to the source. **Metadata.** EXIF, GPS coordinates, XMP, ICC profiles, IPTC blocks and comments are removed. When a file will not re-encode smaller, this is done **losslessly** — the entropy-coded image data is copied through byte for byte and only the metadata segments are dropped, so the pixels are bit-identical to the original. No WebAssembly is involved anywhere. The hosting platform admits binaries only from a vetted allowlist and no image codec is on it, so every codec above is interpreted JavaScript over typed arrays. ## The anti-enlargement rule From PicLite's compression strategy, and the behaviour most often mistaken for a bug: if the re-encode comes out **larger** than the source, the app walks the quality (or the palette) down a few rungs looking for something smaller; if nothing it tries is smaller, it hands back **the original file, untouched**. A row reading "kept" with 0% change means the file was already at or below what a browser-side encoder can reach. Returning a bigger file would have been the wrong answer. ## Everything else it does - Drag-and-drop, multi-select, clipboard paste, and `ss.drive.pick()` over the user's own SkillSafe cloud storage. - Format conversion between JPEG, PNG and WebP, with an explicit matte colour when transparency has to be flattened for JPEG. - Proportional scaling 0.1–100%, a max long-side cap, a halve button, and a never-enlarge guard. The floor is one pixel on each side. - Text watermarks: position (four corners, centre, or tiled), size as a percentage of the long side, rotation, opacity, colour and drop shadow. - Before/after comparison in three modes — side by side, wipe, and blink — with zoom, fit and 1:1. - Per-file and whole-batch savings; the whole batch downloads as a single `.zip` with a `report.md` of what every file did inside it, and individual or paced separate saves are still available. The archive uses the ZIP **stored** method (no deflate) on purpose: every member is already a compressed image, so a deflate pass would spend a second compression over incompressible data to save nothing. The point of the archive is delivery — one click and one folder instead of forty download events — not bytes. - CSV, JSON and Markdown reports of what every file did, including the files that failed and the ones that were handed back untouched. - Four built-in starting presets — Email attachment (long edge 1600, quality 70), Web page (WebP quality 78, long edge 2000), Screenshot / UI (PNG cut to a 64-colour palette with dithering off) and Keep it sharp (quality 92, no resize). They cannot be deleted, and a saved preset of the same name shadows the built-in. - Saved presets and batch history, kept in the signed-in user's SkillSafe account and mirrored in the browser. **Only names, sizes and outcomes are stored — never the images.** ## Honest limits - The JPEG and WebP encoders are the browser's, not `mozjpeg` or a hand-tuned `libwebp`. An already well-optimised JPEG may not shrink further. - Progressive JPEG output is not available; the canvas does not expose it. - Lossless PNG re-crushing is only partly reproduced — filters are chosen adaptively and indices packed tightly, but filter strategies are not brute-forced the way `oxipng` does. - Converting an **animated** GIF to JPEG, PNG or WebP exports the **first frame only**, and the result is labelled that way. Animated WebP output is not supported. - Limits, enforced up front with a reason rather than by stalling: 60 files per batch, 25 MB per file, 40 megapixels per still, 300 frames and 12 megapixels per animation. ## Cost and access Completely free. There is no model in this app, so there is no metered run path, no credit estimate and no charge of any kind. It works fully signed out; signing in only makes saved presets and batch history follow the account to another device. ## Pages - [Pic Lite](https://pic-lite.skillsafe.ai/) — the application. - [API](https://pic-lite.skillsafe.ai/api.html) — the platform endpoints this app's origin exposes, in cURL, Python, JavaScript, Go, Java, Ruby, PHP and C#. - [Licence](https://pic-lite.skillsafe.ai/LICENSE.txt) — GPL-3.0 and the derived-work notice.