{"id":17147490,"url":"https://github.com/jwagner/dont-crop","last_synced_at":"2025-08-22T01:32:50.562Z","repository":{"id":46978100,"uuid":"373321253","full_name":"jwagner/dont-crop","owner":"jwagner","description":"A small, dependency free javascript library to fit a gradient to an image or extract it's primary colors.","archived":false,"fork":false,"pushed_at":"2024-05-18T10:57:08.000Z","size":1420,"stargazers_count":139,"open_issues_count":0,"forks_count":5,"subscribers_count":4,"default_branch":"main","last_synced_at":"2024-12-15T23:46:15.511Z","etag":null,"topics":["color","gfx","image-processing","javascript","typescript"],"latest_commit_sha":null,"homepage":"https://29a.ch/sandbox/2021/dont-crop/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jwagner.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-06-02T22:44:51.000Z","updated_at":"2024-11-30T15:02:40.000Z","dependencies_parsed_at":"2024-10-30T21:03:47.743Z","dependency_job_id":"d0246eeb-4d90-474b-8f81-4de70d656582","html_url":"https://github.com/jwagner/dont-crop","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jwagner%2Fdont-crop","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jwagner%2Fdont-crop/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jwagner%2Fdont-crop/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jwagner%2Fdont-crop/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jwagner","download_url":"https://codeload.github.com/jwagner/dont-crop/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230547678,"owners_count":18243227,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["color","gfx","image-processing","javascript","typescript"],"created_at":"2024-10-14T21:24:57.420Z","updated_at":"2024-12-20T07:07:11.463Z","avatar_url":"https://github.com/jwagner.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"\u003cimg src=\"docs/logo.png\" width=\"400\" /\u003e\n\n[API Documentation](https://29a.ch/sandbox/2021/dont-crop/docs/modules.html) | [Demo](https://29a.ch/sandbox/2021/dont-crop/) \n\n[![Tests](https://github.com/jwagner/dont-crop/actions/workflows/tests.yml/badge.svg)](https://github.com/jwagner/dont-crop/actions/workflows/tests.yml) [![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/)\n\nDont-crop is a [small](#performance), dependency free javascript library to fit a gradient to an image or extract it's primary colors.\n\nIt can be used to pad images instead of cropping them, for a very compact [blur up](https://engineering.fb.com/2015/08/06/android/the-technology-behind-preview-photos/) and what ever else you can come up with.\n\n## Examples\n\n### fitGradient()\n![fitGradient](docs/fitGradient.webp)\nPhoto by [Abed Ismail](https://unsplash.com/photos/fZXZ1-hbFrY)\n\n### getPalette()\n![getPalette](docs/getPalette.webp)\n\n### More Examples\n\nView the [demo page](https://29a.ch/sandbox/2021/dont-crop/)  to see more examples and experiment with your own images.\n\n## Installation\n```\nnpm install -S dont-crop\n```\n\n## Usage\n\n### ES Modules\n```javascript\nimport {getPalette, fitGradient} from 'dont-crop';\n\nconst image = new Image();\n// the image needs to be loaded before you can pass it to dont-crop\nimage.onload = () =\u003e {\n  console.log(getPalette(image));\n  // ['#000000', ...]\n  console.log(fitGradient(image));\n  // 'linear-gradient(#000000, #ffffff)`\n}\nimage.src = 'example.jpg';\n```\n\n### CommonJS\n\n```javascript\nconst getPalette = require('dont-crop').getPalette;\nconst fitGradient = require('dont-crop').fitGradient;\n// ...\n```\n\n### React\nSee [examples/react/index.tsx](examples/react/index.tsx) for a simple example.\n\n### NodeJS\nUsage with node depends on the image processing library being used.\nIn general an image data object needs to be constructed and passed to\n`getPaletteFromImageData` or `fitGradientToImageData`.\n\nThe base functions `getPalette` and `fitGradient` will not work using NodeJS.\nAt least not without bending over backwards.\n\nSee `getImageData` in [examples/node-sharp/example.ts](examples/node-sharp/example.ts) for an example using sharp.\n\n## Compatibility\n\nThe code should run in all common modern browsers and node from version 12 on.\nIt has been tested in:\n* Chrome\n* Firefox\n* Safari\n* Edge\n\n## Performance\n\nThe code is reasonably compact and built with tree shaking in mind.\nSo your bundles will only include the features you actually use.\n\nWhen using `fitGradient` only and bundling your code using webpack 5 dont-crop will add about **1.2 kb** (0.7 gzipped) to your bundle size.\n`getPalette` will cost you a bit more than **3.2 kb** (1.7 gzipped).\nYou can use both for about **4 kb** (2 gzipped).\n\n```\n3925 dist/both.js\n1911 dist/both.js.gz\n1264 dist/fitGradient.js\n710  dist/fitGradient.js.gz\n3261 dist/getPalette.js\n1656 dist/getPalette.js.gz\n```\n\nRuntime performance is also fast enough not to worry about.\n\n```\n# on a AMD Ryzen 9 5950X\nfitGradientToImageData x 19,813 ops/sec ±0.96% (97 runs sampled)\ngetPaletteFromImageData(fast=false) x 156 ops/sec ±0.66% (83 runs sampled)\ngetPaletteFromImageData(fast=true) x 645 ops/sec ±0.17% (97 runs sampled)\n```\n\nThe versions of the functions operating on images rather than the already downscaled image data are slower.\nTheir performance depends on the exact browser and device in question as well but it should generally be in the ballpark of a few milliseconds for reasonably sized images.\n\n## Test Coverage\n\nThe code is well covered in tests. The examples are used as end to end tests in both node and a browser (chrome via puppeteer).\n\n\n## Algorithms\n\nGlad you asked. `fitGradient()` is using simple [linear regression](https://en.wikipedia.org/wiki/Linear_regression).\n\n`getPalette()` is based on [k-means](https://en.wikipedia.org/wiki/K-means_clustering).\nThe initial clusters are chosen using a histogram.\nSimilar clusters in the result are merged in a post processing step.\nThis is necessary because k-means tends to return equally sized clusters\nwhereas getPalette is supposed to return distinct clusters.\nThe merging is tuned to preserve different hues and colors rather than returning the most prominent shades of color (which might all share a similar hue).\nThe processing happens in the CIE Lab color space using CIE76 ΔE*.\n\n\n## Alternatives\n\n### [Just blur the image](https://developer.mozilla.org/en-US/docs/Web/CSS/filter-function/blur())\n\nusing a blurred version of the image as background is a very simple alternative.\nI think it looks a bit more busy but it requires less plumbing (just a bit of css) and will generally be faster.\n\nIf the gradient fitting is performed on the backend and cached or server side rendered this reverses and it becomes a very efficient approximation of the image.\n\n### [colorthief](https://github.com/lokesh/color-thief)\n\nProvides similar functionality to getPalette.\nIt weighs in at about 6.4k (version 2.3.2).\nIt's been widely used since 2019 so it is definitely more battle proofen.\nFrom a quick looks it seems to be using median-cut which will likely yield a bit better results than the simplistic k-means used here.\n\n### [fast-average-color](https://github.com/fast-average-color/fast-average-color)\n\nReturns a single average or dominant color color.\n\n  \n### [smartcrop.js](https://github.com/jwagner/smartcrop.js)\n\nSmartcrop.js is another project of mine. As the name suggests it tries\nto find smarter crops.\n\n## Roadmap\n\nThere are plenty of interesting ways to improve this library further.\n\n* Grouping of colors (saturated, muted, light, dark, warm, cold)\n* Tuning of the variables involved in palette extraction potentially allowing some degree of tweaking by the user of the library\n* Weighting the linear-regression and k-means to focus on the center or edges\n* Using a more robust regression variation like Theil-Senn\n* Gamma corrected linear gradients by manually interpolating the stops\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjwagner%2Fdont-crop","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjwagner%2Fdont-crop","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjwagner%2Fdont-crop/lists"}