nodeca

Stars and mentions

Last 30 days, Oct 5 – Nov 3

GitHub stars14
mentions1
01234Oct 5Oct 19Nov 3
UTC days · repositories nodeca owns3 repositories

Package downloads

Cumulative recorded downloads · since 2025-10-20

npm20.6B
05.2B10.3B15.5B20.6BOct 5Oct 19Nov 3
Oct 19npm: 1/2 packages observed

Across 2 known packages in repositories nodeca owns. Cumulative totals include recorded downloads before the selected range. Missing observations leave gaps; totals may be incomplete.

Top repositories

nodeca’s repositories ranked by new stars, as on trending.

Loading

Contributes to

Repositories outside nodeca’s account where nodeca opened or reviewed pull requests in the past 365 days.

Who noticed nodeca

Everyone who starred or forked nodeca’s repositories since the archive began in 2011.

Notable accounts

Ordered by the stars their own repositories have received.

Biggest fans

Accounts that starred the most of these repositories.

Breakout weeks

Each repository’s best week of new stars, with the release published around it.

Mentions

1post
Nov 40 posts
  1. Why canvas.toBlob() Doesn't Compress PNGs, and What Actually Works in the Browser I run a small browser-based file converter, and for a while my PNG compressor had a quiet bug: it did not compress anything. The code looked reasonable: canvas.toBlob((blob) => resolve(blob), 'image/png', 0.8); Draw the image to a canvas, export it as PNG with quality 0.8, done. The output was usually the same size as the input, and sometimes bigger. Here is why, and what I replaced it with. The quality argument only applies to lossy formats The quality parameter of toBlob() and toDataURL() is only used for lossy formats such as image/jpeg and image/webp. For image/png the browser ignores it. You get whatever its built-in PNG encoder produces, and you have no control over filters or compression level. So canvas.toBlob(cb, 'image/png', 0.8) is just a re-encode. If the source PNG was already written by a decent encoder, you gain nothing. Where the savings in a PNG actually are A PNG is a pixel grid, a per-row filter, and DEFLATE on top. There are two ways to make the file smaller: 1. Lossless re-encoding. Try better filters and a stronger DEFLATE setting. The pixels stay identical, but savings are usually small, often single-digit to low double-digit percent. 2. Palette quantization. Reduce the image from 32-bit RGBA to an indexed palette of up to 256 colors, keeping the alpha channel. This is lossy, but for screenshots, logos, icons and UI graphics it is usually invisible, and the files get much smaller. Tools like TinyPNG get most of their savings from option 2. Doing it in the browser with UPNG.js and pako UPNG.js is a small PNG encoder/decoder from the Photopea author. It uses pako for DEFLATE. Its encode() takes raw RGBA data and a color count: 0 means lossless, 256 means quantize to a 256-color palette. <script src="/assets/js/pako.min.js"></script> <script src="/assets/js/UPNG.js"></script> function compressPng(file, mode) { return new Promise((resolve, reject) => { const img = new Image(); const url = URL.createObjectURL(file); img.onload = () => { const canvas = document.createElement('canvas'); canvas.width = img.naturalWidth; canvas.height = img.naturalHeight; const ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0); const rgba = ctx.getImageData(0, 0, canvas.width, canvas.height).data.buffer; const candidates = [UPNG.encode([rgba.slice(0)], canvas.width, canvas.height, 0)]; if (mode === 'smart') { candidates.push(UPNG.encode([rgba.slice(0)], canvas.width, canvas.height, 256)); } const best = candidates.reduce((a, b) => (b.byteLength < a.byteLength ? b : a)); URL.revokeObjectURL(url); // never hand back a file bigger than the original resolve(best.byteLength < file.size ? new Blob([best], { type: 'image/png' }) : file); }; img.onerror = () => { URL.revokeObjectURL(url); reject(new Error('Could not read PNG')); }; img.src = url; }); } A few details that matter: - getImageData() returns non-premultiplied RGBA, which is what UPNG expects, so transparency survives. - UPNG.encode() may use the buffer you pass it, so pass rgba.slice(0) when you encode twice. - Comparing the candidates and falling back to the original file means users never get a "compressed" file that is larger. What the numbers look like I tested a 1000x700 synthetic image, a noisy gradient with a transparent strip at the top. This is a hard case, not a typical one: | Mode | Size | |---|---| | Original | 1.50 MB | | Lossless (cnum = 0) | 1.43 MB (−5%) | | 256-color palette | 231 KB (−85%) | The lossless number is the honest result of re-encoding: a few percent. The big win comes from quantization. Your own images will vary, so treat these as an illustration and not a benchmark. When not to quantize Palette reduction can cause banding in smooth gradients and photos. For those, you have better options: - Keep lossless mode available, so users who need pixel-exact output can choose it. - Convert photos to JPEG or WebP. canvas.toBlob(cb, 'image/webp', 0.8) does honor the quality value, and WebP supports transparency. - Reduce dimensions if the image is larger than it will ever be displayed. I ended up with two modes in the UI: "Smart" (tries both and keeps the smaller) and "Lossless". Privacy comes for free Because everything above runs in the browser, the image never leaves the device. No upload, no server to trust, nothing to delete afterwards. That was the reason I built the tool in the first place, and it is a real advantage for screenshots with private data or unreleased designs. If you want to see the result, the compressor is here: oneweeb.com. It is free, with no sign-up. Takeaways - quality is ignored for image/png in toBlob(). - Real PNG savings come from palette quantization, not from re-encoding. - Offer a lossless mode, compare outputs, and never return a bigger file than the input. - Client-side processing keeps user files private. Have you run into other canvas export surprises? I would like to hear about them in the comments.

Achievements

Awards earned by the repositories nodeca owns. Select an award to embed its badge.

No awards yet

Repositories earn awards by reaching the top three in a completed trending day or week.

A repository earns an award for finishing first, second or third in a completed trending day or week, ranked by new stars, new forks or pull request authors. Awards follow a repository through renames and transfers and count toward its current owner. Weeks run Monday to Sunday, UTC.