npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

timesnap-core

v0.3.3

Published

Take screenshots of web pages at smooth intervals

Downloads

23

Readme

timesnap

timesnap is a Node.js program that records screenshots of web pages that use JavaScript animations. It uses timeweb and puppeteer to open a web page, overwrite its time-handling functions, and record snapshots at virtual times. For some web pages, this allows frames to be recorded slower than real time, while appearing smooth and consistent when recreated into a video.

It requires Node v8.9.0 or higher and npm.

timesnap-core

timesnap-core is a version of timesnap that does not automatically bundle puppeteer. It differs from timesnap by requiring a config.launcher function or a config.browser object to be passed, and does not have a command line interface. It's stored on the core branch of timesnap and derived from its code. All pull requests should be based on the main branch of timesnap instead of this branch, unless in the rare event that it's particular only to this branch.

To record screenshots and compile them into a video, see timecut and timecut-core. For using virtual time in browser, see timeweb.

# timeweb and timesnap Limitations

timeweb (and timesnap 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.

Read Me Contents

# From Node.js

# Node Install

npm install timesnap-core --save

# Node Examples

For these examples, we'll use puppeteer version 2.1.1, which requires fewer additional libraries to be installed.

npm install [email protected] --save

# Basic Use:

Specify a config.launcher function that creates a browser instance with certain launch options.

const timesnap = require('timesnap-core');
const puppeteer = require('puppeteer');
timesnap({
  launcher: launchOptions => puppeteer.launch(launchOptions),
  url: 'https://tungs.github.io/truchet-tiles-original/#autoplay=true&switchStyle=random',
  viewport: {
    width: 800,               // sets the viewport (window size) to 800x600
    height: 600
  },
  selector: '#container',     // crops each frame to the bounding box of '#container'
  left: 20, top: 40,          // further crops the left by 20px, and the top by 40px
  right: 6, bottom: 30,       // and the right by 6px, and the bottom by 30px
  fps: 30,                    // saves 30 frames for each virtual second
  duration: 20,               // for 20 virtual seconds
  outputDirectory: 'frames'   // to frames/001.png... frames/600.png
                              // of the current working directory
}).then(function () {
  console.log('Done!');
});

# Using config.browser:

You can also use config.browser, though it might ignore / disable some launch options like config.quiet, config.logToStdErr, config.headless, config.executablePath, and config.launchArguments. You can specify custom launch arguments through puppeteer.launch().

const timesnap = require('timesnap-core');
const puppeteer = require('puppeteer');
timesnap({
  browser: puppeteer.launch({ dumpio: true }), // can add custom launch options here
  url: 'https://tungs.github.io/truchet-tiles-original/#autoplay=true&switchStyle=random',
  outputDirectory: 'frames'
}).then(function () {
  console.log('Done!');
});

# Node API

timesnap(config)

  • # config <Object>
    • # launcher <function(Object)> A function that returns or resolves a puppeteer or puppeteer-like browser. It is passed a launch options argument, which should be passed to puppeteer.launch, if possible.
    • # browser <Object> The instance of a puppeteer or puppeteer-like browser. Note that certain configuration options might not work as intended or might be ignored, like config.quiet, config.logToStdErr, config.headless, config.executablePath, and config.launchArguments.
    • # url <string> 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).
    • # outputDirectory <string> Saves images to a directory. Makes one if necessary.
    • # outputPattern <string> Sets each file name according to a printf-style pattern (e.g. image-%03d.png)
    • # fps <number> Frame rate, in frames per virtual second, of capture (default: 60).
    • # duration <number> Duration of capture, in seconds (default: 5).
    • # frames <number> Number of frames to capture. Overrides default fps or default duration.
    • # selector <string> Crops each frame to the bounding box of the first item found by the specified CSS selector.
    • # viewport <Object>
      • # width <number> Width of viewport, in pixels (default: 800).
      • # height <number> Height of viewport, in pixels (default: 600).
      • # deviceScaleFactor <number> Device scale factor (default: 1). Note that the captured image resolution is multiplied by the device scale factor.
      • # isMobile <boolean> Specifies whether the meta viewport tag should be used (default: false).
      • # hasTouch <boolean> Specifies whether the viewport supports touch (default: false).
      • # isLandscape <boolean> Specifies whether the viewport is in landscape mode (default: false).
    • # canvasCaptureMode <boolean | string>
      • Experimental. Captures images from canvas data instead of screenshots. See 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, otherwise it defaults to the first canvas in the document.
    • # start <number> Runs code for config.start virtual seconds before saving any frames (default: 0).
    • # xOffset <number> X offset of capture, in pixels (default: 0).
    • # yOffset <number> Y offset of capture, in pixels (default: 0).
    • # width <number> Width of capture, in pixels.
    • # height <number> Height of capture, in pixels.
    • # transparentBackground <boolean> Allows background to be transparent if there is no background styling.
    • # roundToEvenWidth <boolean> Rounds capture width up to the nearest even number.
    • # roundToEvenHeight <boolean> Rounds capture height up to the nearest even number.
    • # left <number> Left edge of capture, in pixels. Equivalent to config.xOffset.
    • # right <number> Right edge of capture, in pixels. Ignored if config.width is specified.
    • # top <number> Top edge of capture, in pixels. Equivalent to config.yOffset.
    • # bottom <number> Bottom edge of capture, in pixels. Ignored if config.height is specified.
    • # unrandomize <boolean | string | number | Array<number>> 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.
    • # executablePath <string> Uses the Chromium/Chrome instance at config.executablePath for puppeteer.
    • # remoteUrl <string> URL of remote Chromium/Chrome instance to connect using puppeteer.connect().
    • # launchArguments <Array <string>> Extra arguments for Puppeteer/Chromium. Example: ['--single-process']. A list of arguments can be found here.
    • # headless <boolean> Runs puppeteer in headless (nonwindowed) mode (default: true).
    • # screenshotType <string> Output image format for the screenshots. By default, the file extension is used to infer type, and failing that, 'png' is used. 'jpeg' is also available.
    • # screenshotQuality <number> Quality level between 0 to 1 for lossy screenshots. Defaults to 0.92 when in canvas capture mode and 0.8 otherwise.
    • # startDelay <number> Waits config.startDelay real seconds after loading before starting (default: 0).
    • # quiet <boolean> Suppresses console logging.
    • # logger <function(...Object)> Replaces console logging with a particular function. The passed arguments are the same as those to console.log (in this case, usually one string).
    • # logToStdErr <boolean> Logs to stderr instead of stdout. Doesn't do anything if config.quiet is set to true.
    • # shouldSkipFrame <function(Object)> A function that determines whether a current frame should be skipped for capturing. It should return true if the current frame should be skipped, false if not. It is passed the following object:
      • # frameCount <number> The current frame count, starting at 1.
      • # framesToCapture <number> The total number of frames to be captured.
      • # page <Page> the puppeteer page.
    • # stopFunctionName <string> function name that the client web page can call to stop capturing. For instance, 'stopCapture' could be called in the client, via stopCapture().
    • # frameProcessor <function(Buffer, number, number)> A function that will be called after capturing each frame. If config.outputDirectory and config.outputPattern aren't specified, enabling this suppresses automatic file output. After capturing each frame, config.frameProcessor is called with three arguments, and if it returns a promise, capture will be paused until the promise resolves:
      • screenshotData <Buffer> A buffer of the screenshot data.
      • frameNumber <number> The current frame number (1 based).
      • totalFrames <number> The total number of frames.
    • # navigatePageToURL <function(Object)> 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:
      • # page <Page> the puppeteer page
      • # url <string> the url to navigate to
    • # preparePage <function(Page)> 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.
      • page <Page> The puppeteer instance of the page being captured.
    • # preparePageForScreenshot <function(Page, number, number)> A setup function that will be called before each screenshot. If it returns a promise, capture will be paused until the promise resolves.
      • page <Page> The puppeteer instance of the page being captured.
      • frameNumber <number> The current frame number (1 based).
      • totalFrames <number> The total number of frames.
  • # returns: <Promise> resolves after all the frames have been captured.

# timesnap Modes

timesnap can capture frames using one of two modes:

  • # 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.
  • # 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. 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 or set config.canvasCaptureMode from Node.js. Also specify the canvas using a css selector, using the --selector option from the command line or setting config.selector from Node.js, otherwise it uses the first canvas element.

# How it works

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.

This work was inspired by a talk by Noah Veltman, 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.