{"id":13565691,"url":"https://github.com/tungs/timecut","last_synced_at":"2025-10-06T17:52:14.344Z","repository":{"id":41394706,"uuid":"122121581","full_name":"tungs/timecut","owner":"tungs","description":"Node.js program to record smooth movies of web pages with JavaScript animations","archived":false,"fork":false,"pushed_at":"2023-07-18T21:24:14.000Z","size":151,"stargazers_count":637,"open_issues_count":32,"forks_count":73,"subscribers_count":18,"default_branch":"main","last_synced_at":"2025-05-16T06:03:45.190Z","etag":null,"topics":["nodejs","puppeteer","video"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tungs.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}},"created_at":"2018-02-19T21:16:10.000Z","updated_at":"2025-04-07T15:40:30.000Z","dependencies_parsed_at":"2024-01-06T01:09:43.315Z","dependency_job_id":"a0f1c735-0a16-435a-a1fb-478a567177b6","html_url":"https://github.com/tungs/timecut","commit_stats":{"total_commits":125,"total_committers":9,"mean_commits":13.88888888888889,"dds":0.264,"last_synced_commit":"b6cb23e1deaf35b84358831f3f0728acb5a36542"},"previous_names":[],"tags_count":24,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tungs%2Ftimecut","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tungs%2Ftimecut/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tungs%2Ftimecut/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tungs%2Ftimecut/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tungs","download_url":"https://codeload.github.com/tungs/timecut/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254478186,"owners_count":22077675,"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":["nodejs","puppeteer","video"],"created_at":"2024-08-01T13:01:53.093Z","updated_at":"2025-10-06T17:52:09.311Z","avatar_url":"https://github.com/tungs.png","language":"JavaScript","funding_links":[],"categories":["JavaScript","Packages"],"sub_categories":[],"readme":"# timecut\n\n**timecut** is a Node.js program that records smooth videos of web pages that use JavaScript animations. It uses **[timeweb](https://github.com/tungs/timeweb)**, **[timesnap](https://github.com/tungs/timesnap)**, and [puppeteer](https://github.com/GoogleChrome/puppeteer) to open a web page, overwrite its time-handling functions, take snapshots of the web page, and then passes the results to ffmpeg to encode those frames into a video. This allows for slower-than-realtime and/or virtual high-fps capture of frames, while the resulting video is smooth.\n\nYou can run **timecut** from the command line or as a Node.js library. It requires ffmpeg, Node v8.9.0 or higher, and npm.\n\nTo only record screenshots and save them as pictures, see **[timesnap](https://github.com/tungs/timesnap)**. For using virtual time in browser, see **[timeweb](https://github.com/tungs/timeweb)**.\n\n## \u003ca name=\"limitations\" href=\"#limitations\"\u003e#\u003c/a\u003e **timeweb**, **timecut**, and **timesnap** Limitations\n**timeweb** (and **timesnap** and **timecut** by extension) only overwrites JavaScript functions and video playback, so pages where changes occur via other means (e.g. through transitions/animations from CSS rules) will likely not render as intended.\n\n## Read Me Contents\n\n* [From the Command Line](#from-cli)\n  * [Global Install and Use](#cli-global-install)\n  * [Local Install and Use](#cli-local-install)\n  * [Command Line *url*](#cli-url-use)\n  * [Command Line Examples](#cli-examples)\n  * [Command Line *options*](#cli-options)\n* [From Node.js](#from-node)\n  * [Node Install](#node-install)\n  * [Node Examples](#node-examples)\n  * [Node API](#node-api)\n* [timecut Modes](#modes)\n* [How it works](#how-it-works)\n\n## \u003ca name=\"from-cli\" href=\"#from-cli\"\u003e#\u003c/a\u003e From the Command Line\n\n### \u003ca name=\"cli-global-install\" href=\"#cli-global-install\"\u003e#\u003c/a\u003e Global Install and Use\n\nTo install:\n\nDue to [an issue in puppeteer](https://github.com/GoogleChrome/puppeteer/issues/375) with permissions, timecut is not supported for global installation for root. You can configure `npm` to install global packages for a specific user following this guide: https://docs.npmjs.com/getting-started/fixing-npm-permissions#option-two-change-npms-default-directory\n\nAfter configuring, run:\n```\nnpm install -g timecut\n```\n\nTo use:\n```\ntimecut \"url\" [options]\n```\n\n### \u003ca name=\"cli-local-install\" href=\"#cli-local-install\"\u003e#\u003c/a\u003e Local Install and Use\n\nTo install:\n\n```\ncd /path/to/installation/directory\nnpm install timecut\n```\n\nTo use:\n```\nnode /path/to/installation/directory/node_modules/timecut/cli.js \"url\" [options]\n```\n\n*Alternatively*:\n\nTo install:\n\n\n```\ncd /path/to/installation/directory\ngit clone https://github.com/tungs/timecut.git\ncd timecut\nnpm install\n```\n\nTo use:\n```\nnode /path/to/installation/directory/timecut/cli.js \"url\" [options]\n```\n\n### \u003ca name=\"cli-url-use\" href=\"#cli-url-use\"\u003e#\u003c/a\u003e Command Line *url*\nThe url can be a web url (e.g. `https://github.com`) or a file path, with relative paths resolving in the current working directory. If no url is specified, defaults to `index.html`. Remember to enclose urls that contain special characters (like `#` and `\u0026`) with quotes.\n\n### \u003ca name=\"cli-examples\" href=\"#cli-examples\"\u003e#\u003c/a\u003e Command Line Examples\n\n**\u003ca name=\"cli-example-default\" href=\"#cli-example-default\"\u003e#\u003c/a\u003e Default behavior**:\n```\ntimecut\n```\nOpens `index.html` in the current working directory, sets the viewport to 800x600, captures at 60 frames per second for 5 virtual seconds (temporarily saving each frame), and saves `video.mp4` with the `yuv420p` pixel format in the current working directory. The defaults may change in the future, so for long-term scripting, it's a good idea to explicitly pass these options, like in the following example.\n\n**\u003ca name=\"cli-example-viewport-fps-duration-mode-output\" href=\"#cli-example-viewport-fps-duration-mode-output\"\u003e#\u003c/a\u003e Setting viewport size, frames per second, duration, mode, and output**:\n```\ntimecut index.html --viewport=\"800,600\" --fps=60 --duration=5 \\\n  --frame-cache --pix-fmt=yuv420p --output=video.mp4\n```\nEquivalent to the current default `timecut` invocation, but with explicit options. Opens `index.html` in the current working directory, sets the viewport to 800x600, captures at 60 frames per second for 5 virtual seconds (temporarily saving each frame), and saves the resulting video using the pixel format `yuv420p` as `video.mp4`.\n\n**\u003ca name=\"cli-example-selector\" href=\"#cli-example-selector\"\u003e#\u003c/a\u003e Using a selector**:\n```\ntimecut drawing.html -S \"canvas,svg\"\n```\nOpens `drawing.html` in the current working directory, crops each frame to the bounding box of the first canvas or svg element, and captures frames using default settings (5 seconds @ 60fps saving to `video.mp4`).\n\n**\u003ca name=\"cli-example-offsets\" href=\"#cli-example-offsets\"\u003e#\u003c/a\u003e Using offsets**:\n```\ntimecut \"https://tungs.github.io/amuse/truchet-tiles/#autoplay=true\u0026switchStyle=random\" \\ \n  -S \"#container\" \\ \n  --left=20 --top=40 --right=6 --bottom=30 \\\n  --duration=20\n```\nOpens https://tungs.github.io/amuse/truchet-tiles/#autoplay=true\u0026switchStyle=random (note the quotes in the url and selector are necessary because of the `#` and `\u0026`). Crops each frame to the `#container` element, with an additional crop of 20px, 40px, 6px, and 30px for the left, top, right, and bottom, respectively. Captures frames for 20 virtual seconds at 60fps to `video.mp4` in the current working directory.\n\n### \u003ca name=\"cli-options\" href=\"#cli-options\"\u003e#\u003c/a\u003e Command Line *options*\n* \u003ca name=\"cli-options-output\" href=\"#cli-options-output\"\u003e#\u003c/a\u003e Output: `-O`, `--output` *name*\n    * Tells ffmpeg to save the video as *name*. Its file extension determines encoding if not explicitly specified.\n* \u003ca name=\"cli-options-fps\" href=\"#cli-options-fps\"\u003e#\u003c/a\u003e Frame Rate: `-R`, `--fps` *frame rate*\n    * Frame rate (in frames per virtual second) of capture (default: `60`).\n* \u003ca name=\"cli-options-duration\" href=\"#cli-options-duration\"\u003e#\u003c/a\u003e Duration: `-d`, `--duration` *seconds*\n    * Duration of capture, in *seconds* (default: `5`).\n* \u003ca name=\"cli-options-frames\" href=\"#cli-options-frames\"\u003e#\u003c/a\u003e Frames: `--frames` *count*\n    * Number of frames to capture.\n* \u003ca name=\"cli-options-selector\" href=\"#cli-options-selector\"\u003e#\u003c/a\u003e Selector: `-S`, `--selector` \"*selector*\"\n    * Crops each frame to the bounding box of the first item found by the [CSS *selector*][CSS selector].\n* \u003ca name=\"cli-options-viewport\" href=\"#cli-options-viewport\"\u003e#\u003c/a\u003e Viewport: `-V`, `--viewport` *dimensions*\n    * Viewport dimensions, in pixels, followed by optional keys. For example, `800` (for width), or `\"800,600\"` (for width and height), or `\"800,600,deviceScaleFactor=2\"` for (width, height, and deviceScaleFactor). When running in Windows, quotes may be necessary for parsing commas. For a list of optional keys, see [`config.viewport`](#js-config-viewport).\n* \u003ca name=\"cli-options-frame-cache\" href=\"#cli-options-frame-cache\"\u003e#\u003c/a\u003e Frame Cache: `--frame-cache` *[directory]*\n    * Saves each frame temporarily to disk before ffmpeg processes it. If *directory* is not specified, temporarily creates one in the current working directory. Enabled by default. See [cache frame mode](#cache-frame-mode).\n* \u003ca name=\"cli-options-pipe-mode\" href=\"#cli-options-pipe-mode\"\u003e#\u003c/a\u003e Pipe Mode: `--pipe-mode`\n    * Experimental. Pipes frames directly to ffmpeg, without saving to disk. See [pipe mode](#pipe-mode).\n* \u003ca name=\"cli-options-canvas-capture-mode\" href=\"#cli-options-canvas-capture-mode\"\u003e#\u003c/a\u003e Canvas Mode: `--canvas-capture-mode` *\\[format\\]*\n    * Experimental. Captures images from canvas data instead of screenshots. See [canvas capture mode](#canvas-capture-mode). Can provide an optional image format (e.g. `png`), otherwise it uses the saved image's extension, or defaults to `png` if the format is not specified or supported. Can prefix the format with `immediate:` (e.g. `immediate:png`) to immediately capture pixel data after rendering, which is sometimes needed for some WebGL renderers. Specify the canvas [using the `--selector` option](#cli-options-selector), otherwise it defaults to the first canvas in the document.\n* \u003ca name=\"cli-options-start\" href=\"#cli-options-start\"\u003e#\u003c/a\u003e Start: `-s`, `--start` *n seconds*\n    * Runs code for n virtual seconds before saving any frames (default: `0`).\n* \u003ca name=\"cli-options-x-offset\" href=\"#cli-options-x-offset\"\u003e#\u003c/a\u003e X Offset: `-x`, `--x-offset` *pixels*\n    * X offset of capture, in pixels (default: `0`).\n* \u003ca name=\"cli-options-y-offset\" href=\"#cli-options-y-offset\"\u003e#\u003c/a\u003e Y Offset: `-y`, `--y-offset` *pixels*\n    * Y offset of capture, in pixels (default: `0`).\n* \u003ca name=\"cli-options-width\" href=\"#cli-options-width\"\u003e#\u003c/a\u003e Width: `-W`, `--width` *pixels*\n    * Width of capture, in pixels.\n* \u003ca name=\"cli-options-height\" href=\"#cli-options-height\"\u003e#\u003c/a\u003e Height: `-H`, `--height` *pixels*\n    * Height of capture, in pixels.\n* \u003ca name=\"cli-options-no-round-to-even-width\" href=\"#cli-options-no-round-to-even-width\"\u003e#\u003c/a\u003e No Even Width Rounding: `--no-round-to-even-width`\n    * Disables automatic rounding of capture width up to the nearest even number.\n* \u003ca name=\"cli-options-no-round-to-even-height\" href=\"#cli-options-no-round-to-even-height\"\u003e#\u003c/a\u003e No Even Height Rounding: `--no-round-to-even-height`\n    * Disables automatic rounding of capture height up to the nearest even number.\n* \u003ca name=\"cli-options-transparent-background\" href=\"#cli-options-transparent-background\"\u003e#\u003c/a\u003e Transparent Background: `--transparent-background`\n    * Allows background to be transparent if there is no background styling. Only works if the output video format supports transparency.\n* \u003ca name=\"cli-options-left\" href=\"#cli-options-left\"\u003e#\u003c/a\u003e Left: `-l`, `--left` *pixels*\n    * Left edge of capture, in pixels. Equivalent to `--x-offset`.\n* \u003ca name=\"cli-options-right\" href=\"#cli-options-right\"\u003e#\u003c/a\u003e Right: `-r`, `--right` *pixels*\n    * Right edge of capture, in pixels. Ignored if `width` is specified.\n* \u003ca name=\"cli-options-top\" href=\"#cli-options-top\"\u003e#\u003c/a\u003e Top: `-t`, `--top` *pixels*\n    * Top edge of capture, in pixels. Equivalent to `--y-offset`.\n* \u003ca name=\"cli-options-bottom\" href=\"#cli-options-bottom\"\u003e#\u003c/a\u003e Bottom: `-b`, `--bottom` *pixels*\n    * Bottom edge of capture, in pixels. Ignored if `height` is specified.\n* \u003ca name=\"cli-options-unrandomize\" href=\"#cli-options-unrandomize\"\u003e#\u003c/a\u003e Unrandomize: `-u`, `--unrandomize` *\\[seeds\\]*\n    * Overwrites `Math.random` with a seeded pseudorandom number generator. Can provide optional seeds as up to four comma separated integers (e.g. `--unrandomize 2,3,5,7` or `--unrandomize 42`). If `seeds` is `random-seed` (i.e. `--unrandomize random-seed`), a random seed will be generated, displayed (if not in quiet mode), and used. If `seeds` is not provided, it uses the seeds `10,0,20,0`.\n* \u003ca name=\"cli-options-executable-path\" href=\"#cli-options-executable-path\"\u003e#\u003c/a\u003e Executable Path: `--executable-path` *path*\n    * Uses the Chromium/Chrome instance at *path* for puppeteer.\n* \u003ca name=\"cli-options-ffmpeg-path\" href=\"#cli-options-ffmpeg-path\"\u003e#\u003c/a\u003e ffmpeg Path: `--ffmpeg-path` *path*\n    * Uses the ffmpeg *path* for running ffmpeg.\n* \u003ca name=\"cli-options-launch-arguments\" href=\"#cli-options-launch-arguments\"\u003e#\u003c/a\u003e Puppeteer Launch Arguments: `-L`, `--launch-arguments` *arguments*\n    * Arguments to pass to Puppeteer/Chromium, enclosed in quotes. Example: `--launch-arguments=\"--single-process\"`. A list of arguments can be found [here](https://peter.sh/experiments/chromium-command-line-switches).\n* \u003ca name=\"cli-options-no-headless\" href=\"#cli-options-no-headless\"\u003e#\u003c/a\u003e No Headless: `--no-headless`\n    * Runs Chromium/Chrome in windowed mode.\n* \u003ca name=\"cli-options-screenshot-type\" href=\"#cli-options-screenshot-type\"\u003e#\u003c/a\u003e Screenshot Type: `--screenshot-type` *type*\n    * Output image format for the screenshots. By default, `png` is used. `jpeg` is also available.\n* \u003ca name=\"cli-options-screenshot-quality\" href=\"#cli-options-screenshot-quality\"\u003e#\u003c/a\u003e Screenshot Quality: `--screenshot-quality` *number*\n    * Quality level between 0 to 1 for lossy screenshots. Defaults to 0.92 when in [canvas capture mode](#cli-options-canvas-capture-mode) and 0.8 otherwise.\n* \u003ca name=\"cli-options-extra-input-options\" href=\"#cli-options-extra-input-options\"\u003e#\u003c/a\u003e Extra input options: `-e`, `--input-options` *options*\n    * Extra arguments for ffmpeg input, enclosed in quotes. Example: `--input-options=\"-framerate 30\"`\n* \u003ca name=\"cli-options-extra-output-options\" href=\"#cli-options-extra-output-options\"\u003e#\u003c/a\u003e Extra output options: `-E`, `--output-options` *options*\n    * Extra arguments for ffmpeg output, enclosed in quotes. Example: `--output-options=\"-vf scale=320:240\"`\n* \u003ca name=\"cli-options-pixel-format\" href=\"#cli-options-pixel-format\"\u003e#\u003c/a\u003e Pixel Format: `--pix-fmt` *pixel format*\n    * Pixel format for output video (default: `yuv420p`).\n* \u003ca name=\"cli-options-start-delay\" href=\"#cli-options-start-delay\"\u003e#\u003c/a\u003e Start Delay: `--start-delay` *n seconds*\n    * Waits *n real seconds* after loading the page before starting to capture.\n* \u003ca name=\"cli-options-keep-frames\" href=\"#cli-options-keep-frames\"\u003e#\u003c/a\u003e Keep Frames: `--keep-frames`\n    * Doesn't delete frames after processing them. Doesn't do anything in pipe mode.\n* \u003ca name=\"cli-options-quiet\" href=\"#cli-options-quiet\"\u003e#\u003c/a\u003e Quiet: `-q`, `--quiet`\n    * Suppresses console logging.\n* \u003ca name=\"cli-options-stop-function-name\" href=\"#cli-options-stop-function-name\"\u003e#\u003c/a\u003e Stop Function Name: `--stop-function-name` *function name*\n    * Creates a function with *function name* that the client web page can call to stop capturing. For instance, `--stop-function-name=stopCapture` could be called in the client, via `stopCapture()`.\n* \u003ca name=\"cli-options-version\" href=\"#cli-options-version\"\u003e#\u003c/a\u003e Version: `-v`, `--version`\n    * Displays version information. Immediately exits.\n* \u003ca name=\"cli-options-help\" href=\"#cli-options-help\"\u003e#\u003c/a\u003e Help: `-h`, `--help`\n    * Displays command line options. Immediately exits.\n\n## \u003ca name=\"from-node\" href=\"#from-node\"\u003e#\u003c/a\u003e From Node.js\n**timecut** can also be included as a library inside Node.js programs.\n\n### \u003ca name=\"node-install\" href=\"#node-install\"\u003e#\u003c/a\u003e Node Install\n```\nnpm install timecut --save\n```\n\n### \u003ca name=\"node-examples\" href=\"#node-examples\"\u003e#\u003c/a\u003e Node Examples\n\n**\u003ca name=\"node-example-basic\" href=\"#node-example-basic\"\u003e#\u003c/a\u003e Basic Use:**\n```node\nconst timecut = require('timecut');\ntimecut({\n  url: 'https://tungs.github.io/amuse/truchet-tiles/#autoplay=true\u0026switchStyle=random',\n  viewport: {\n    width: 800,               // sets the viewport (window size) to 800x600\n    height: 600\n  },\n  selector: '#container',     // crops each frame to the bounding box of '#container'\n  left: 20, top: 40,          // further crops the left by 20px, and the top by 40px\n  right: 6, bottom: 30,       // and the right by 6px, and the bottom by 30px\n  fps: 30,                    // saves 30 frames for each virtual second\n  duration: 20,               // for 20 virtual seconds \n  output: 'video.mp4'         // to video.mp4 of the current working directory\n}).then(function () {\n  console.log('Done!');\n});\n```\n\n**\u003ca name=\"node-example-multiple\" href=\"#node-example-multiple\"\u003e#\u003c/a\u003e Multiple pages:**\n```node\nconst timecut = require('timecut');\nvar pages = [\n  {\n    url: 'https://tungs.github.io/amuse/truchet-tiles/#autoplay=true',\n    output: 'truchet-tiles.mp4',\n    selector: '#container'\n  }, {\n    url: 'https://breathejs.org/examples/Drawing-US-Counties.html',\n    output: 'counties.mp4',\n    selector: null // with no selector, it defaults to the viewport dimensions\n  }\n];\n(async () =\u003e {\n  for (let page of pages) {\n    await timecut({\n      url: page.url,\n      output: page.output,\n      selector: page.selector,\n      viewport: {\n        width: 800,\n        height: 600\n      },\n      duration: 20\n    });\n  }\n})();\n```\n\n### \u003ca name=\"node-api\" href=\"#node-api\"\u003e#\u003c/a\u003e Node API\n\nThe Node API is structured similarly to the command line options, but there are a few options for the Node API that are not accessible through the command line interface: [`config.logToStdErr`](#js-config-log-to-std-err), [`config.navigatePageToURL`](#js-config-navigate-page-to-url), [`config.preparePage`](#js-config-prepare-page), [`config.preparePageForScreenshot`](#js-config-prepare-page-for-screenshot), [`config.outputStream`](#js-config-output-stream), [`config.logger`](#js-config-logger), and certain [`config.viewport`](#js-config-viewport) properties.\n\n**timecut(config)**\n*  \u003ca name=\"js-api-config\" href=\"#js-api-config\"\u003e#\u003c/a\u003e `config` \u0026lt;[Object][]\u0026gt;\n    * \u003ca name=\"js-config-url\" href=\"#js-config-url\"\u003e#\u003c/a\u003e `url` \u0026lt;[string][]\u0026gt; The url to load. It can be a web url, like `https://github.com` or a file path, with relative paths resolving in the current working directory (default: `index.html`).\n    * \u003ca name=\"js-config-output\" href=\"#js-config-output\"\u003e#\u003c/a\u003e `output` \u0026lt;[string][]\u0026gt; Tells ffmpeg to save the video as *name*.  Its file extension determines encoding if not explicitly specified. Default name: `video.mp4`.\n    * \u003ca name=\"js-config-fps\" href=\"#js-config-fps\"\u003e#\u003c/a\u003e `fps` \u0026lt;[number][]\u0026gt; frame rate, in frames per virtual second, of capture (default: `60`).\n    * \u003ca name=\"js-config-duration\" href=\"#js-config-duration\"\u003e#\u003c/a\u003e `duration` \u0026lt;[number][]\u0026gt; Duration of capture, in seconds (default: `5`).\n    * \u003ca name=\"js-config-frames\" href=\"#js-config-frames\"\u003e#\u003c/a\u003e `frames` \u0026lt;[number][]\u0026gt; Number of frames to capture. Overrides default fps or default duration.\n    * \u003ca name=\"js-config-selector\" href=\"#js-config-selector\"\u003e#\u003c/a\u003e `selector` \u0026lt;[string][]\u0026gt; Crops each frame to the bounding box of the first item found by the specified [CSS selector][].\n    * \u003ca name=\"js-config-frame-cache\" href=\"#js-config-frame-cache\"\u003e#\u003c/a\u003e `frameCache` \u0026lt;[string][]|[boolean][]\u0026gt; Saves each frame temporarily to disk before ffmpeg processes it. If `config.frameCache` is a string, uses that as the directory to save the temporary files. If `config.frameCache` is a boolean `true`, temporarily creates a directory in the current working directory. See [cache frame mode](#cache-frame-mode).\n    * \u003ca name=\"js-config-pipe-mode\" href=\"#js-config-pipe-mode\"\u003e#\u003c/a\u003e `pipeMode` \u0026lt;[boolean][]\u0026gt; Experimental. If set to `true`, pipes frames directly to ffmpeg, without saving to disk. See [pipe mode](#pipe-mode).\n    * \u003ca name=\"js-config-viewport\" href=\"#js-config-viewport\"\u003e#\u003c/a\u003e `viewport` \u0026lt;[Object][]\u0026gt;\n        * \u003ca name=\"js-config-viewport-width\" href=\"#js-config-viewport-width\"\u003e#\u003c/a\u003e `width` \u0026lt;[number][]\u0026gt; Width of viewport, in pixels (default: `800`).\n        * \u003ca name=\"js-config-viewport-height\" href=\"#js-config-viewport-height\"\u003e#\u003c/a\u003e `height` \u0026lt;[number][]\u0026gt; Height of viewport, in pixels (default: `600`).\n        * \u003ca name=\"js-config-viewport-scale-factor\" href=\"#js-config-viewport-scale-factor\"\u003e#\u003c/a\u003e `deviceScaleFactor` \u0026lt;[number][]\u0026gt; Device scale factor (default: `1`).\n        * \u003ca name=\"js-config-viewport-mobile\" href=\"#js-config-viewport-mobile\"\u003e#\u003c/a\u003e `isMobile` \u0026lt;[boolean][]\u0026gt; Specifies whether the `meta viewport` tag should be used (default: `false`).\n        * \u003ca name=\"js-config-viewport-touch\" href=\"#js-config-viewport-touch\"\u003e#\u003c/a\u003e `hasTouch` \u0026lt;[boolean][]\u0026gt; Specifies whether the viewport supports touch (default: `false`).\n        * \u003ca name=\"js-config-viewport-landscape\" href=\"#js-config-viewport-landscape\"\u003e#\u003c/a\u003e `isLandscape` \u0026lt;[boolean][]\u0026gt; Specifies whether the viewport is in landscape mode (default: `false`).\n    * \u003ca name=\"js-config-canvas-capture-mode\" href=\"#js-config-canvas-capture-mode\"\u003e#\u003c/a\u003e `canvasCaptureMode` \u0026lt;[boolean][] | [string][]\u0026gt;\n        * Experimental. Captures images from canvas data instead of screenshots. See [canvas capture mode](#canvas-capture-mode). Can provide an optional image format (e.g. `png`), otherwise it uses the saved image's extension, or defaults to `png` if the format is not specified or supported. Can prefix the format with `immediate:` (e.g. `immediate:png`) to immediately capture pixel data after rendering, which is sometimes needed for some WebGL renderers. Specify the canvas by [setting `config.selector`](#js-config-selector), otherwise it defaults to the first canvas in the document.\n    * \u003ca name=\"js-config-start\" href=\"#js-config-start\"\u003e#\u003c/a\u003e `start` \u0026lt;[number][]\u0026gt; Runs code for `config.start` virtual seconds before saving any frames (default: `0`).\n    * \u003ca name=\"js-config-x-offset\" href=\"#js-config-x-offset\"\u003e#\u003c/a\u003e `xOffset` \u0026lt;[number][]\u0026gt; X offset of capture, in pixels (default: `0`).\n    * \u003ca name=\"js-config-y-offset\" href=\"#js-config-y-offset\"\u003e#\u003c/a\u003e `yOffset` \u0026lt;[number][]\u0026gt; Y offset of capture, in pixels (default: `0`).\n    * \u003ca name=\"js-config-width\" href=\"#js-config-width\"\u003e#\u003c/a\u003e `width` \u0026lt;[number][]\u0026gt; Width of capture, in pixels.\n    * \u003ca name=\"js-config-height\" href=\"#js-config-height\"\u003e#\u003c/a\u003e `height` \u0026lt;[number][]\u0026gt; Height of capture, in pixels.\n    * \u003ca name=\"js-config-transparent-background\" href=\"#js-config-transparent-background\"\u003e#\u003c/a\u003e `transparentBackground` \u0026lt;[boolean][]\u0026gt; Allows background to be transparent if there is no background styling. Only works if the output video format supports transparency.\n    * \u003ca name=\"js-config-round-to-even-width\" href=\"#js-config-round-to-even-width\"\u003e#\u003c/a\u003e `roundToEvenWidth` \u0026lt;[boolean][]\u0026gt; Rounds capture width up to the nearest even number (default: `true`).\n    * \u003ca name=\"js-config-round-to-even-height\" href=\"#js-config-round-to-even-height\"\u003e#\u003c/a\u003e `roundToEvenHeight` \u0026lt;[boolean][]\u0026gt; Rounds capture height up to the nearest even number (default: `true`).\n    * \u003ca name=\"js-config-left\" href=\"#js-config-left\"\u003e#\u003c/a\u003e `left` \u0026lt;[number][]\u0026gt; Left edge of capture, in pixels. Equivalent to `config.xOffset`.\n    * \u003ca name=\"js-config-right\" href=\"#js-config-right\"\u003e#\u003c/a\u003e `right` \u0026lt;[number][]\u0026gt; Right edge of capture, in pixels. Ignored if `config.width` is specified.\n    * \u003ca name=\"js-config-top\" href=\"#js-config-top\"\u003e#\u003c/a\u003e `top` \u0026lt;[number][]\u0026gt; Top edge of capture, in pixels. Equivalent to `config.yOffset`.\n    * \u003ca name=\"js-config-bottom\" href=\"#js-config-bottom\"\u003e#\u003c/a\u003e `bottom` \u0026lt;[number][]\u0026gt; Bottom edge of capture, in pixels. Ignored if `config.height` is specified.\n    * \u003ca name=\"js-config-unrandomize\" href=\"#js-config-unrandomize\"\u003e#\u003c/a\u003e `unrandomize` \u0026lt;[boolean][] | [string][] | [number][] | [Array][]\u0026lt;[number][]\u0026gt;\u0026gt; Overwrites `Math.random` with a seeded pseudorandom number generator. If it is a number, an array of up to four numbers, or a string of up to four comma separated numbers, then those values are used as the initial seeds. If it is true, then the default seed is used. If it is the string 'random-seed', a random seed will be generated, displayed (if quiet mode is not enabled), and used.\n    * \u003ca name=\"js-config-executable-path\" href=\"#js-config-executable-path\"\u003e#\u003c/a\u003e `executablePath` \u0026lt;[string][]\u0026gt; Uses the Chromium/Chrome instance at `config.executablePath` for puppeteer.\n    * \u003ca name=\"js-config-ffmpeg-path\" href=\"#js-config-ffmpeg-path\"\u003e#\u003c/a\u003e `ffmpegPath` \u0026lt;[string][]\u0026gt; Uses the ffmpeg *path* for running ffmpeg.\n    * \u003ca name=\"js-config-launch-arguments\" href=\"#js-config-launch-arguments\"\u003e#\u003c/a\u003e `launchArguments` \u0026lt;[Array][] \u0026lt;[string][]\u0026gt;\u0026gt; Extra arguments for Puppeteer/Chromium. Example: `['--single-process']`. A list of arguments can be found [here](https://peter.sh/experiments/chromium-command-line-switches).\n    * \u003ca name=\"js-config-headless\" href=\"#js-config-headless\"\u003e#\u003c/a\u003e `headless` \u0026lt;[boolean][]\u0026gt; Runs puppeteer in headless (nonwindowed) mode (default: `true`).\n    * \u003ca name=\"js-config-screenshot-type\" href=\"#js-config-screenshot-type\"\u003e#\u003c/a\u003e `screenshotType` \u0026lt;[string][]\u0026gt; Output image format for the screenshots. By default, `'png'` is used. `'jpeg'` is also available.\n    * \u003ca name=\"js-config-screenshot-quality\" href=\"#js-config-screenshot-quality\"\u003e#\u003c/a\u003e `screenshotQuality` \u0026lt;[number][]\u0026gt; Quality level between 0 to 1 for lossy screenshots. Defaults to 0.92 when in [canvas capture mode](#js-config-canvas-capture-mode) and 0.8 otherwise.\n    * \u003ca name=\"js-config-input-options\" href=\"#js-config-input-options\"\u003e#\u003c/a\u003e `inputOptions` \u0026lt;[Array][] \u0026lt;[string][]\u0026gt;\u0026gt; Extra arguments for ffmpeg input. Example: `['-framerate', '30']`\n    * \u003ca name=\"js-config-output-options\" href=\"#js-config-output-options\"\u003e#\u003c/a\u003e `outputOptions` \u0026lt;[Array][] \u0026lt;[string][]\u0026gt;\u0026gt; Extra arguments for ffmpeg output. Example: `['-vf', 'scale=320:240']`\n    * \u003ca name=\"js-config-pixel-format\" href=\"#js-config-pixel-format\"\u003e#\u003c/a\u003e `pixFmt` \u0026lt;[string][]\u0026gt; Pixel format for output video (default: `yuv420p`).\n    * \u003ca name=\"js-config-start-delay\" href=\"#js-config-start-delay\"\u003e#\u003c/a\u003e `startDelay` \u0026lt;[number][]\u0026gt; Waits `config.startDelay` real seconds after loading before starting (default: `0`).\n    * \u003ca name=\"js-config-keep-frames\" href=\"#js-config-keep-frames\"\u003e#\u003c/a\u003e `keepFrames` \u0026lt;[boolean][]\u0026gt; If set to true, doesn't delete frames after processing them. Doesn't do anything in pipe mode.\n    * \u003ca name=\"js-config-quiet\" href=\"#js-config-quiet\"\u003e#\u003c/a\u003e `quiet` \u0026lt;[boolean][]\u0026gt; Suppresses console logging.\n    * \u003ca name=\"js-config-logger\" href=\"#js-config-logger\"\u003e#\u003c/a\u003e `logger` \u0026lt;[function][](...[Object][])\u0026gt; Replaces console logging with a particular function. The passed arguments are the same as those to `console.log` (in this case, usually one string).\n    * \u003ca name=\"js-config-log-to-std-err\" href=\"#js-config-log-to-std-err\"\u003e#\u003c/a\u003e `logToStdErr` \u0026lt;[boolean][]\u0026gt; Logs to stderr instead of stdout. Doesn't do anything if `config.quiet` is set to true.\n    * \u003ca name=\"js-config-stop-function-name\" href=\"#js-config-stop-function-name\"\u003e#\u003c/a\u003e `stopFunctionName` \u0026lt;[string][]\u0026gt; *function name* that the client web page can call to stop capturing. For instance, `'stopCapture'` could be called in the client, via `stopCapture()`.\n    * \u003ca name=\"js-config-navigate-page-to-url\" href=\"#js-config-navigate-page-to-url\"\u003e#\u003c/a\u003e `navigatePageToURL` \u0026lt;[function][]([Object][])\u0026gt; A function that navigates a puppeteer page to a URL, overriding the default navigation to a URL. The function should return a promise that resolves once the page is finished navigating. The function is passed the following object:\n        * \u003ca name=\"js-config-navigate-page-to-url-page\" href=\"#js-config-navigate-page-to-url-page\"\u003e#\u003c/a\u003e `page` \u0026lt;[Page][]\u0026gt; the puppeteer page\n        * \u003ca name=\"js-config-navigate-page-to-url-url\" href=\"#js-config-navigate-page-to-url-url\"\u003e#\u003c/a\u003e `url` \u0026lt;[string][]\u0026gt; the url to navigate to\n    * \u003ca name=\"js-config-prepare-page\" href=\"#js-config-prepare-page\"\u003e#\u003c/a\u003e `preparePage` \u0026lt;[function][]([Page][])\u0026gt; A setup function that will be called one time before taking screenshots. If it returns a promise, capture will be paused until the promise resolves.\n        * `page` \u0026lt;[Page][]\u0026gt; The puppeteer instance of the page being captured.\n    * \u003ca name=\"js-config-prepare-page-for-screenshot\" href=\"#js-config-prepare-page-for-screenshot\"\u003e#\u003c/a\u003e `preparePageForScreenshot` \u0026lt;[function][]([Page][], [number][], [number][])\u0026gt; A setup function that will be called before each screenshot. If it returns a promise, capture will be paused until the promise resolves.\n        * `page` \u0026lt;[Page][]\u0026gt; The puppeteer instance of the page being captured.\n        * `frameNumber` \u0026lt;[number][]\u0026gt; The current frame number (1 based).\n        * `totalFrames` \u0026lt;[number][]\u0026gt; The total number of frames.\n    * \u003ca name=\"js-config-output-stream\" href=\"#js-config-output-stream\"\u003e#\u003c/a\u003e `outputStream` \u0026lt;[stream][]()\u0026gt; A node stream to write data to from ffmpeg\n    * \u003ca name=\"js-config-output-stream-options\" href=\"#js-config-output-stream-options\"\u003e#\u003c/a\u003e `outputStreamOptions` \u0026lt;[Object][]\u0026gt; Optional configuration object when using [`config.outputStream`](#js-config-output-stream)\n        * \u003ca name=\"js-config-output-stream-options-format\" href=\"#js-config-output-stream-options-format\"\u003e#\u003c/a\u003e `format` \u0026lt;[string][]\u0026gt; Format of piped output. Defaults to `'mp4'` if undefined.\n        * \u003ca name=\"js-config-output-stream-options-movflags\" href=\"#js-config-output-stream-options-movflags\"\u003e#\u003c/a\u003e `movflags` \u0026lt;[string][]\u0026gt; String representing MOV muxer flags to pass via `-movflags` argument. Defaults to `'frag_keyframe+empty_moov+faststart'` if undefined.\n* \u003ca name=\"js-api-return\" href=\"#js-api-return\"\u003e#\u003c/a\u003e returns: \u0026lt;[Promise][]\u0026gt; resolves after all the frames have been captured.\n\n## \u003ca name=\"modes\" href=\"#modes\"\u003e#\u003c/a\u003e **timecut** Modes\n### \u003ca name=\"capture-modes\" href=\"#capture-modes\"\u003e#\u003c/a\u003e Capture Modes\n**timecut** can capture frames to using one of two modes:\n  * \u003ca name=\"screenshot-capture-mode\" href=\"#screenshot-capture-mode\"\u003e#\u003c/a\u003e **Screenshot capture mode** (default) uses puppeteer's built-in API to take screenshots of Chromium/Chrome windows. It can capture most parts of a webpage (e.g. div, svg, canvas) as they are rendered on the webpage. It can crop images, round to even widths/heights, but it usually runs slower than canvas capture mode.\n  * \u003ca name=\"canvas-capture-mode\" href=\"#canvas-capture-mode\"\u003e#\u003c/a\u003e **Canvas capture mode** (experimental) directly copies data from a canvas element and is often faster than using screenshot capture mode. If the background of the canvas is transparent, it may show up as transparent or black depending on the captured image format and the output video format. Configuration options that adjust the crop and round to an even width/height do not currently have an effect. To use this mode, [use the `--canvas-capture-mode` option from the command line](#cli-options-canvas-capture-mode) or [set `config.canvasCaptureMode` from Node.js](#js-config-canvas-capture-mode). Also specify the canvas using a css selector, [using the `--selector` option from the command line](#cli-options-selector) or [setting `config.selector` from Node.js](#js-config-selector), otherwise it uses the first canvas element.\n### \u003ca name=\"frame-transfer-modes\" href=\"#frame-transfer-modes\"\u003e#\u003c/a\u003e Frame Transfer Modes\n**timecut** can pass frames to ffmpeg using one of two modes:\n  * \u003ca name=\"cache-frame-mode\" href=\"#cache-frame-mode\"\u003e#\u003c/a\u003e **Cache frame mode** stores each frame temporarily before running ffmpeg on all of the images. This mode can use a lot of temporary disk space (hundreds of megabytes per second of recorded time), but takes up less memory and is more stable than [pipe mode](#pipe-mode). This is currently enabled by default, though it may change in the future. To explicitly use this mode, [use the `--frame-cache` option from the command line](#cli-options-frame-cache) or [set `config.frameCache` from Node.js](#js-config-frame-cache) to `true` or to a directory name.\n  * \u003ca name=\"pipe-mode\" href=\"#pipe-mode\"\u003e#\u003c/a\u003e **Pipe mode** (experimental) pipes each frame directly to `ffmpeg`, without saving each frame. This takes up less temporary space than [cache frame mode](#cache-frame-mode), but it currently has some observed stability issues. To use this mode, [use the `--pipe-mode` option from the command line](#cli-options-pipe-mode) or [set `config.pipeCache` to `true` from Node.js](#js-config-pipe-mode). If you run into issues, you may want to try [cache frame mode](#cache-frame-mode) or to install and use **timesnap** and [pipe it directly to ffmpeg](https://github.com/tungs/timesnap#cli-example-piping). Both alternative implementations seem more stable than the current pipe mode.\n\n## \u003ca name=\"how-it-works\" href=\"#how-it-works\"\u003e#\u003c/a\u003e How it works\n**timecut** uses **[timesnap](https://github.com/tungs/timesnap)** to record frames to send to `ffmpeg`. **timesnap** uses puppeteer's `page.evaluateOnNewDocument` feature to automatically overwrite a page's native time-handling JavaScript functions and objects (`new Date()`, `Date.now`, `performance.now`, `requestAnimationFrame`, `setTimeout`, `setInterval`, `cancelAnimationFrame`, `cancelTimeout`, and `cancelInterval`) to custom ones that use a virtual timeline, allowing for JavaScript computation to complete before taking a screenshot.\n\nThis work was inspired by [a talk by Noah Veltman](https://github.com/veltman/d3-unconf), who described altering a document's `Date.now` and `performance.now` functions to refer to a virtual time and using `puppeteer` to change that virtual time and take snapshots.\n\n[Object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object\n[Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array\n[Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise\n[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type\n[number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type\n[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type\n[function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions\n[CSS selector]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors\n[Page]: https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#class-page\n[stream]: https://nodejs.org/api/stream.html\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftungs%2Ftimecut","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftungs%2Ftimecut","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftungs%2Ftimecut/lists"}