Pure-JS image resize, crop and thumbnails with Jimp (no Sharp): working versions (tested 2026-10-15)
Create thumbnails with Jimp (no native deps): cover-crop to a square, resize keeping aspect ratio, and re-encode PNG/JPEG buffers, tested with generated images.
passed4 passed · 0 failed2026-10-15 13:23 UTC- Status
- passed
- Last passed
- 2026-10-15 13:23 UTC
- Tests
- 4 / 4
- Language
- TypeScript
- Runtime
- node v22.23.3
pinned versions3
jimp@1.6.1tsx@4.23.15@types/node@24.19.2
run it
npm install --no-audit --no-fund --ignore-scripts=false npm test
test environment
node v22.23.3 linux 6.17.0-1022-azure x64 runner: github-actions 38741369695
The code
Exactly the files that passed. Your agent can pull them with get_recipe("jimp-image-resize") (install), or see them on GitHub.
package.json
{
"name": "jimp-image-resize",
"private": true,
"type": "module",
"scripts": {
"test": "node --import tsx --test test/*.test.ts"
},
"dependencies": {
"jimp": "1.6.1"
},
"devDependencies": {
"tsx": "4.23.15",
"@types/node": "24.19.2"
}
}
src/thumbs.ts
import { Jimp, JimpMime } from "jimp";
export async function squareThumb(input: Buffer, size = 128) {
const img = await Jimp.read(input);
img.cover({ w: size, h: size }); // scale + center-crop
return img.getBuffer(JimpMime.png);
}
export async function fitWidth(input: Buffer, width: number, quality = 80) {
const img = await Jimp.read(input);
if (img.width > width) img.resize({ w: width }); // height follows aspect ratio
return img.getBuffer(JimpMime.jpeg, { quality });
}
export async function size(buf: Buffer) {
const img = await Jimp.read(buf);
return { w: img.width, h: img.height, mime: img.mime };
}
test/thumbs.test.ts
import { test } from "node:test";
import assert from "node:assert/strict";
import { Jimp, JimpMime } from "jimp";
import { fitWidth, size, squareThumb } from "../src/thumbs.ts";
const makePng = (w: number, h: number) => new Jimp({ width: w, height: h, color: 0x1ed67fff }).getBuffer(JimpMime.png);
test("square thumbnail from a wide image", async () => {
assert.deepEqual(await size(await squareThumb(await makePng(400, 200), 64)), { w: 64, h: 64, mime: "image/png" });
});
test("fit width keeps aspect ratio and outputs jpeg", async () => {
assert.deepEqual(await size(await fitWidth(await makePng(1000, 500), 250)), { w: 250, h: 125, mime: "image/jpeg" });
});
test("does not upscale small images", async () => {
assert.equal((await size(await fitWidth(await makePng(100, 50), 250))).w, 100);
});
test("rejects non-images", async () => {
await assert.rejects(squareThumb(Buffer.from("not an image")));
});
Connect to ProvenPin
Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.
terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp
~/.cursor/mcp.json
{ "mcpServers": { "provenpin": { "url": "https://mcp.provenpin.com/mcp" } } }.vscode/mcp.json
{ "servers": { "provenpin": { "type": "http", "url": "https://mcp.provenpin.com/mcp" } } }~/.codeium/windsurf/mcp_config.json
{ "mcpServers": { "provenpin": { "serverUrl": "https://mcp.provenpin.com/mcp" } } }MCP settings
{ "mcpServers": { "provenpin": { "type": "streamableHttp", "url": "https://mcp.provenpin.com/mcp" } } }Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.