{"id":13475873,"url":"https://github.com/rsify/pico","last_synced_at":"2025-04-07T23:12:59.925Z","repository":{"id":38004001,"uuid":"248884319","full_name":"rsify/pico","owner":"rsify","description":"Take browser screenshots in Javascript  📸","archived":false,"fork":false,"pushed_at":"2024-06-04T03:08:49.000Z","size":3765,"stargazers_count":1978,"open_issues_count":27,"forks_count":44,"subscribers_count":14,"default_branch":"master","last_synced_at":"2025-03-31T22:24:22.025Z","etag":null,"topics":["dom-to-image","fluture","fp-ts","html-to-image","html2canvas","javascript","typescript"],"latest_commit_sha":null,"homepage":"","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/rsify.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":"contributing.md","funding":null,"license":"license","code_of_conduct":".github/code_of_conduct.md","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":"2020-03-21T01:20:48.000Z","updated_at":"2025-03-25T19:21:22.000Z","dependencies_parsed_at":"2024-01-06T07:58:02.688Z","dependency_job_id":"f61c84b6-fb42-43eb-9a89-116716e8b580","html_url":"https://github.com/rsify/pico","commit_stats":null,"previous_names":["nikersify/pico"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rsify%2Fpico","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rsify%2Fpico/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rsify%2Fpico/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rsify%2Fpico/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rsify","download_url":"https://codeload.github.com/rsify/pico/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247744335,"owners_count":20988783,"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":["dom-to-image","fluture","fp-ts","html-to-image","html2canvas","javascript","typescript"],"created_at":"2024-07-31T16:01:24.297Z","updated_at":"2025-04-07T23:12:59.884Z","avatar_url":"https://github.com/rsify.png","language":"TypeScript","readme":"\u003cdiv align=\"center\"\u003e\n\u003cbr\u003e\n\u003cimg height=\"200px\" src=\"https://github.com/gripeless/pico/blob/master/media/pico-shadow.png?raw=true\"\u003e\n\u003ch3\u003e📸 Pico\u003c/h3\u003e\n\u003cp\u003eTake browser screenshots in Javascript\u003c/p\u003e\n\u003cimg src=\"https://img.shields.io/npm/v/@gripeless/pico\" alt=\"npm\"\u003e\n\u003cimg src=\"https://img.shields.io/github/issues/gripeless/pico\" alt=\"GitHub issues\"\u003e\n\u003cimg src=\"https://img.shields.io/bundlephobia/minzip/@gripeless/pico?label=compressed\" alt=\"Compressed size\"\u003e\n\n\u003c/div\u003e\n\n\n\u003cbr\u003e\n\n\u003cdiv\u003e\n\t\u003cimg width=\"49%\" src=\"https://github.com/gripeless/pico/blob/master/media/wikipedia-real.png?raw=true\"\u003e\n\t\u003cimg width=\"50%\" src=\"https://github.com/gripeless/pico/blob/master/media/wikipedia-pico.png?raw=true\"\u003e\n\u003c/div\u003e\n\n\u003cdiv\u003e\n\t\u003cimg width=\"49%\" src=\"https://github.com/gripeless/pico/blob/master/media/firebase-real.png?raw=true\"\u003e\n\t\u003cimg width=\"50%\" src=\"https://github.com/gripeless/pico/blob/master/media/firebase-pico.png?raw=true\"\u003e\n\u003c/div\u003e\n\n\u003cdiv\u003e\n\t\u003cimg width=\"49%\" src=\"https://github.com/gripeless/pico/blob/master/media/gripeless-real.png?raw=true\"\u003e\n\t\u003cimg width=\"50%\" src=\"https://github.com/gripeless/pico/blob/master/media/gripeless-pico.png?raw=true\"\u003e\n\u003c/div\u003e\n\n\u003cdiv align=\"center\"\u003e\u003csub\u003e(Original page on the left · PNG output on the right)\u003c/sub\u003e\u003c/div\u003e\n\n\u003cbr\u003e\n\n---\n\n\n# Goal\n\nPico's goal is to produce high precision screenshots of any viewport entirely\nclient side. This is different from simply capturing a webpage using\n[Puppeteer](https://github.com/puppeteer/puppeteer) or a similar tool in that\n**the screenshot taking happens entirely client side**.\n\nThe viewport screenshots include scrolled element scroll states, cross-origin\nimages, input states, web fonts, canvas contents, current video frame contents,\nand much more information that you wouldn't be able to get using something like\na headless browser.\n\nAt the time of writing there are no existing solutions that are aimed\nof reproducing the entire viewport accurately like Pico.\n\n\n# How it works\n\n\u003e Warning: nerdy\n\nThis program renders whatever is displayed in the given `Window` into an\nimage, thanks to svg's `\u003cforeignObject\u003e`.\n\n**No server side code is required** to produce the screenshot.\n\nThere is no native Javascript API to take the screenshot of what the user is\ncurrently seeing on their screen (and because of security issues there\nprobably will never be one).\n\nSince we don't have access to the raw data that's being shown to the user we\nhave to reconstruct it manually. This program works thanks to svg's\n`\u003cforeignObject\u003e` which lets you insert any valid HTML content inside, which\nwe can then pass as a data URL into a `\u003ccanvas\u003e`' `drawImage` and read out\nthe raw image data with `canvas.toBlob` or `canvas.toDataURL`.\n\nThe above alone would work great in a universe where subresources didn't\nexist - which as you know is not our universe. SVG's inserted into `\u003cimg\u003e`\ntags (or in our case, `\u003ccanvas\u003e`') cannot display any external resources,\nwhether it's images, fonts or stylesheets.\n\nTo work around that fact Pico does the following things:\n- Downloads and inlines contents of all `\u003cimg\u003e` tags as data URL's in their `src`\n  attributes\n- Downloads external stylesheets and inlines them as `\u003cstyle\u003e` tags\n- Checks all stylesheets for nested resources\n\t- Downloads and checks nested stylesheets in `@import` rules\n\t- Downloads any resources referenced by the `url()` function, including\n\t  but not exclusive to the following properties:\n\t\t- `background`s\n\t\t- `background-image`s\n\t\t- `src` in `@font-face` rule\n\t\t- `cursor`\n\t\t- `content`\n\nIn addition, Pico also:\n- Copies input states (text inputs, checkboxes, textareas) into `value`\n  attributes so that they can be shown in SVG\n- Emulates current scroll positions on all scrolled elements (including the\n  root `\u003chtml\u003e` element) via either `transform: translate` (for root node)\n  and `absolute` positioning of children of scrolled nodes\n- Transforms existing `\u003ccanvas\u003e` elements into `\u003cimg\u003e` tags with the contents of the `\u003ccanvas\u003e`' inlined as data URL's in `src`\n- Performs various minor fixes for `rem` font size, working media queries,\n  preserving size of everything, etc.\n\nThe returned DOM is inserted into an `\u003ciframe\u003e`, serialized into XML,\nconverted into a data URL, put into an `Image`, which is then rendered onto\na `\u003ccanvas\u003e` whose contents are read out with `canvas.toBlob` and finally\nreturned to the program's caller, together with all the errors when\nresources failed to load.\n\nPico is able to safely accumulate all async resource errors thanks to\n[Fluture](https://github.com/fluture-js/Fluture), which is a really great\nalternative to the native `Promise` and forces you to write type safe\nerrors. You can read a [fantastic introductory article to it by the\nlibrary's author here](https://dev.to/avaq/fluture-a-functional-alternative-to-promises-21b).\n\n\n# API\n\nPico is built using [Fluture](https://github.com/fluture-js/Fluture) and in\naddition to the `Promise` also provides a direct API to `Fluture` via functions\nsuffixed with `Fluture`. If you don't care about functional programming just\nuse the non-suffixed functions to work with `Promise`'s instead.\n\nAll functions return an \"`ErrorStack`\", which is basically just the returned\nvalue paired with any errors that happened while computing it. Most errors will\nbe CORS or 404 related issues when loading subresources.\n\n## Types\n\n```typescript\ndeclare type ErrorStack\u003cT\u003e = {\n    errors: DetailedError[];\n    value: T;\n};\n```\n\n```typescript\nexport declare type DetailedError = {\n    // Human readable string of why the error happened\n    reason: string;\n\n    // Proper error object\n    error: Error;\n};\n```\n\n```typescript\nexport declare type Options = {\n    // An array of selectors to nodes that should not be included in the output.\n    ignore: string[];\n};\n```\n\n## Functions\n\n```typescript\ndeclare const objectURL: ($window: Window, partialOptions?: Partial\u003cOptions\u003e) =\u003e Promise\u003cErrorStack\u003cstring\u003e\u003e;\ndeclare const objectURLFluture: ($window: Window, options: Options) =\u003e Fluture\u003cDetailedError, ErrorStack\u003cstring\u003e\u003e;\n```\nRender the given `Window` to a PNG image and return it as an\n[object URL](https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL).\nThis is safer to use than `dataURL` due to memory constraints. Remember to call\n[`URL.revokeObjectURL`](https://developer.mozilla.org/en-US/docs/Web/API/URL/revokeObjectURL)\nwhen you're done with the image.\n\n---\n\n```typescript\ndeclare const dataURL: ($window: Window, partialOptions?: Partial\u003cOptions\u003e) =\u003e Promise\u003cErrorStack\u003cstring\u003e\u003e;\ndeclare const dataURLFluture: ($window: Window, options: Options) =\u003e Fluture\u003cDetailedError, ErrorStack\u003cstring\u003e\u003e;\n```\nRender the given `Window` to a PNG image and return it as a\n[data url](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs).\nNote that\n[in Chrome the limit for data url's is 2MB](https://stackoverflow.com/a/41755526),\nprefer `objectURL` when possible.\n\n---\n\n```typescript\ndeclare const svgObjectURL: ($window: Window, partialOptions?: Partial\u003cOptions\u003e) =\u003e Promise\u003cErrorStack\u003cstring\u003e\u003e;\ndeclare const svgObjectURLFluture: ($window: Window, options: Options) =\u003e Fluture\u003cDetailedError, ErrorStack\u003cstring\u003e\u003e;\n```\n\nRender the given `Window` to an SVG image and return it as an\n[object URL](https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL).\nThis function is mainly useful for inspecting the output of Pico using\ndevtools, for real uses prefer the other functions.\n\n# Installation\n\n```bash\n$ npm install @gripeless/pico\n```\n\nThe module is intended to be used exclusively in the browser via a code bundler\nlike Rollup or Webpack. There is no single file bundle build provided at this\ntime.\n\n\n# Contributing\n\nSee [contributing.md](contributing.md).\n\n\n# Caveats\n\nPico is being developed against recent Firefox and Blink based browsers\n(Chrome, Opera, Brave, Edge). It does not work on Safari or old Edge versions\ndue to lack of proper support for `\u003cforeignObject\u003e`.\n\n\n# Prior art\n\nPico's code was inspired in many ways by the following libraries:\n\n- [dom-to-image](https://github.com/tsayen/dom-to-image) (and its sisters [dom-to-image-more](https://github.com/1904labs/dom-to-image-more) and [html-to-image](https://github.com/bubkoo/html-to-image#readme))\n- [rasterizeHTML.js](https://github.com/cburgmer/rasterizeHTML.js)\n- [html2canvas](https://github.com/niklasvh/html2canvas)\n\nPico's selling point is representing the whole viewport\nas accurately as possible. If you want to render a single DOM node instead,\nconsider using one of the above libraries.\n\nTo the authors of the above code, thank you for your awesome work.\n\n# License\n\nMIT\n","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frsify%2Fpico","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frsify%2Fpico","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frsify%2Fpico/lists"}