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 🙏

© 2026 – Pkg Stats / Ryan Hefner

time-widget

v0.0.33

Published

<h1 align="center"> <br> TimeWidget <br> </h1> <h4 align="center">A tool that summarizes and explores temporal data sets with quantitative values</h4> You can use it to visualize thousands of time series, and select multiple groups by direct m

Readme

| Group Selection | Detailed View | |:--------------------------------------------------------------------------------------------------------------:|:-----------------------------------------------------------------------------------------------------------------------:| | | | | Moving Patterns | Reference Lines | | | |

Try it!

You can test TimeWidget right now with your own CSV data (less than 200MB), using:

| Observable Notebook | |-----------------------------------------------------------------------------------------------------------------------------------------------------------------------| | |

Examples of functionalities.

Real World Examples.

Install

npm install time-widget

Requires ^[email protected], ^[email protected] and ^[email protected].

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
</head>
<body>
<!-- target for the main Widget -->
<h1>Stock Prices</h1>
<div id="target"></div>

<!-- Load the libraries -->
<!-- <script src="../dist/TimeWidget.js"></script> -->
<script src="https://d3js.org/d3.v7.js"></script>
<script src="https://unpkg.com/time-widget/dist/TimeWidget.min.js"></script>

<script>
    let data = [
        { Date: new Date("01/01/2023"), Open: 250, id: "Apple", group: "Technology" },
        { Date: new Date("01/02/2023"), Open: 240, id: "Apple", group: "Technology" },
        { Date: new Date("01/03/2023"), Open: 260, id: "Apple", group: "Technology" },
    ];

    let ts = TimeWidget(
            data,
            {
                x: "Date", // Attribute to show in the X axis (Note that it also supports functions)
                y: "Open", // Attribute to show in the Y axis (Note that it also supports functions)
                id: "stock", // Attribute to group the input data (Note that it also supports functions)
            }
    );

    ts.addEventListener("input", () => {
        console.log("Selected", ts.value);
    });

    document.getElementById("target").appendChild(ts);
</script>
</body>
</html>

Step by step

  1. HTML. Start with this template
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <!-- Your overview widget goes here -->
  <div id="target"></div>
  <!-- tYour detailed widget goes here -->
  <div id="targetDetailed"></div>
</body>
</html>
  1. Import TimeWidget. Create and import a new JavaScript file below the scripts (d3, and TimeWidget) or right in the html like in the example below.
<script src="https://d3js.org/d3.v7.js"></script>
<script src="https://unpkg.com/time-widget/dist/TimeWidget.min.js"></script>
<script type="text/javascript">
  //   YOUR_JS_CODE_HERE
</script>
  1. Create a TimeWidget Instance

  let data = [
    { Date: "01/01/2023", Open: 250, id: "Apple", group: "Technology" },
    { Date: "01/02/2023", Open: 240, id: "Apple", group: "Technology" },
    { Date: "01/03/2023", Open: 260, id: "Apple", group: "Technology" },
  ];

   let target = TimeWidget(data, {        
      x: "Date", // Atribute to show in the X axis (Note that it also supports functions)
      y:  "Open", // Atribute to show in the Y axis (Note that it also supports functions)
      id: "stock", // Atribute to group the input data (Note that it also supports functions)
      color: "Group", // (Optional) Attribute to color by
   });

  target.addEventListener("input", () => {console.log("Selected", target.value.selectedIds)})
  1. [Optional] Configure TimeWidget render

You have two options: add them at initialization:

   let target = TimeWidget(data, {
    x: "Date", // Atribute to show in the X axis (Note that it also supports functions)
    y:  "Open", // Atribute to show in the Y axis (Note that it also supports functions)
    id: "stock", // Atribute to group the input data (Note that it also supports functions)
    color: "Group", // (Optional) Attribute to color by
    
    xPartitions: 10, // Partitions performed on the X-axis for the collision acceleration algorithm.
    yPartitions: 10, // Partitions performed on the Y-axis for the collision acceleration algorithm.
    defaultAlpha: 0.8, // Default transparency (when no selection is active) of drawn lines
    selectedAlpha: 1, // Transparency of selected lines
    noSelectedAlpha: 0.4, // Transparency of unselected lines
    backgroundColor: "#ffffff"
});

Or as a subsequent step after initialization

   // Default Parameters
   target.ts.xPartitions = 10; // Partitions performed on the X-axis for the collision acceleration algorithm.
   target.ts.yPartitions = 10; // Partitions performed on the Y-axis for the collision acceleration algorithm.
   target.ts.defaultAlpha = 0.8; // Default transparency (when no selection is active) of drawn lines
   target.ts.selectedAlpha = 1; // Transparency of selected lines
   target.ts.noSelectedAlpha = 0.4; // Transparency of unselected lines
   target.ts.backgroundColor = "#ffffff";
  1. **[Optional] Add the references lines
    target.ts.addReferenceCurves(myReferenceCurves)

For the definition of the reference lines, see the custom formats section.

Options

This section will show all possible options grouped by categories.

Elements

  • target: pass a html element where you want to render
  • showBrushesControls:If true, the brush control is displayed in the default location. If false you can still use brushesControlsElement to show the control on a different element on your app. For this use the exported value "groups"
  • showBrushTooltip: Allows to display a tooltip on the brushes containing its coordinates.
  • showBrushesCoordinates: If false you can still use brushesCoordinatesElement to show the control on a different element on your app. For this use the exported value "brushesCoordinates"
  • showDataGroups: If false you can still use dataGroupElement to show the control on a different element on your app. For this use the exported value "dataGroups"
  • showDetails: If false and with hasDetails = true, you can still use detailsElement to show the control on a different element on your app. For this use the exported value "details"

Data

  • x: Attribute to show in the X axis (Note that it also supports functions)
  • y: Attribute to show in the Y axis (Note that it also supports functions)
  • id: Attribute to group the input data (Note that it also supports functions)
  • color: Specifies the attribute to be used to discriminate the groups (Note that it also supports functions).
  • xScale: It allows to pass a scale of d3 with its parameters, except for the domain which is defined by the xDomain parameter.
  • yScale: It allows to pass a scale of d3 with its parameters, except for the domain which is defined by the yDomain parameter.
  • xDomain: Defines the domain to be used in the x scale.
  • yDomain: Defines the domain to be used in the y scale.
  • referenceCurves: Specifies a Json object with the information of the reference lines.
  • fmtX: Function, how to format x points in the tooltip. Note that it must conform to the data type provided in X.
  • fmtY: Function, how to format x points in the tooltip. Note that it must conform to the data type provided in Y.
  • snapX: Optional positive interval in X domain units to which both horizontal brush edges are snapped. For time scales, use a single-unit date-fns Duration such as { months: 1 } or { days: 7 }, or a number of milliseconds. Calendar durations are applied from the lower X-domain boundary with date-fns, so months and years preserve their real calendar boundaries. The visible upper domain boundary is expanded when necessary so the domain width is an exact multiple of the interval.
  • snapY: Optional positive interval in Y domain units to which both vertical brush edges are snapped. The grid starts at the lower Y-domain boundary, and the visible upper boundary is expanded to the next interval when necessary.
  • xLabel: Label to show in the X axis
  • yLabel: Label to show in the Y axis
  • xTicks: Allows to use custom strings as ticks on the X-axis independently of the X-scale. A vector of [xValue,Label] pairs is expected. Note that only the defined elements are displayed and xValue must belong to the domain of X.
  • yTicks: Allows to use custom strings as ticks on the y-axis independently of the y-scale. A vector of [yValue,Label] pairs is expected. Note that only the defined elements are displayed and yValue must belong to the domain of X.
  • filters: Array of predefined TimeGroups and TimeBoxes. Example

For example, this constrains every TimeBox edge to multiples of 15 on X and 5 on Y:

const target = TimeWidget(data, {
  snapX: 15,
  snapY: 5,
});

Color Configuration

  • defaultAlpha: Default transparency (when no selection is active) of drawn lines
  • selectedAlpha: Transparency of selected lines
  • noSelectedAlpha: Transparency of unselected lines
  • alphaScale: A scale to adjust the alpha by the number of rendering elements
  • backgroundColor:
  • defaultColor: Default color (when no selection is active) of the drawn lines. It only has effect when "color" is not defined.
  • selectedColor: Color of selected lines. It only has effect when "color" is not defined.
  • noSelectedColor: Color of unselected lines. It only has effect when "color" is not defined.
  • colorScale: The color scale to be used to display the different groups defined by the "color" attribute. Typically, a categorical scale of D3
  • brushesColorScale: The color scale to be used to display the brushes typically a categorical scale of D3
  • selectedColorTransform: Function to be applied to the color of the selected group. It only has effect when "color" is defined.

size Configuration

  • width: Set the desired width of the overview Widget
  • height: Set the desired height of the overview Widget
  • detailsContainerHeight: Set the desired height of the details container Widget
  • detailsWidth: Set the desired width of the individual details visualization
  • detailsHeight: Set the desired height of the individual details visualization
  • margin: Set the desired margin for overview Widget, d3 common format ( { left: 50, top: 30, bottom: 50, right: 20 })
  • detailsMargin: Margin options for details view, d3 common format, leave null for using the overview margin

CallBacks

  • updateCallback: (data) => doSomethingWithData
  • statusCallback: (status) => doSomethingWithStatus

Performance measurements

For repeatable browser measurements, enable the lightweight monitor and move a brush as usual. Every reportEvery measured frames it reports sustained FPS and the p50/p95/p99 percentiles for frame time, collision detection, rendering and total processing time. Times are expressed in milliseconds. FPS p5 and p1 correspond to the slow-tail frame-time p95 and p99 respectively.

const target = TimeWidget(data, {
  x: "Date",
  y: "Open",
  id: "stock",
  performanceMonitoring: {
    maxSamples: 300,
    reportEvery: 60,
    log: true,
  },
});

target.ts.performance.reset(); // start a clean experimental run
const report = target.ts.performance.report(); // JSON-serializable results

// Or run 300 rendering frames automatically and receive the final report:
const automaticReport = await target.ts.performance.run({ frames: 300 });

// Include selection/collision work by moving a brush automatically:
const brushReport = await target.ts.performance.runBrush({
  frames: 300,
  warmupFrames: 60,
  cycles: 3,
  brushHeight: 0.25,
});

Use performanceMonitoring: false to disable the instrumentation. An onReport(report) callback can be supplied instead of (or in addition to) the console output. runBrush() replaces the current filters with a deterministic brush and moves it horizontally, so its report also includes selection and collision timings without user interaction. The first warmupFrames frames are executed but discarded from all metrics (60 by default).

Rendering

  • brushShadow: Determines how the shadow will be applied to the TimeBoxes belonging to the active TimeGroup.
  • showGroupMedian: If active show a line with the median of the enabled groups.
  • hasDetails: Determines whether detail data will be displayed or not. Disabling it saves preprocessing time if detail data is not to be displayed.
  • doubleYlegend: Allows the y-axis legend to be displayed on both sides of the chart.
  • showGrid: If active, a reference grid is displayed.
  • brushGroupSize: Controls the size of the colored rectangles used to select the different brushGroups.

Performance

  • maxDetailsRecords: How many results to show in the detail view
  • maxTimelines: Set to a value to limit the number of distinct timelines to show
  • xPartitions: Partitions performed on the X-axis for the collision acceleration algorithm.
  • yPartitions: Partitions performed on the Y-axis for the collision acceleration algorithm.

Options

  • medianNumBins: Number of bins used to compute the group median.
  • medianLineDash: Selected group median line dash pattern canvas style
  • medianLineAlpha: Selected group median line opacity
  • medianLineWidth: Selected group median line width
  • medianFn: Function to use when showing the median
  • medianMinRecordsPerBin: Min number of records each bin must have to be considered
  • autoUpdate: Allows to decide whether changes in brushes are processed while moving, or only at the end of the movement.
  • _this: pass the object this in order to be able to maintain the state in case of changes in the input
  • fixAxis: When active, the axes will not change when modifying the data.

Custom Formats

This section details the different formats used by the application for some parameters. Note that the fields marked as optional are not mandatory and if not provided a default value will be used.

Reference lines

[
  {
    "name": "Line1",
    "color": "yellow", // Color in css format
    "opacity": 1, // opacity level of the line
    "data": [[p1x,p1y],[p2x,p2y],...]
  },
  {
    "name": "Line2",
    "color": "red", // Color in css format
    "opacity": 0.5, // opacity level of the line
    "data": [[p1x,p1y],[p2x,p2y],...]
  }
]  

Filters

filters: [
    {
        name: "Group 1",
        IsEnable: true, /*Optional*/
        isActive: false, /*Optional*/
        brushes: [
            {
                selectionDomain: [
                  [1.5,-7], /*[x0,y0]*/
                  [2.5, -17] /*[x1,y1]*/  
                ],
                mode: "intersect", /* or "contains". Optional*/
                aggregation: "and", /* or "or". Optional */
            }
        ]
    },
    {
        Another BrushGroup
    }
]

Contributing

npm ci
npm run build        # rollup -> dist/{TimeWidget.js,.esm.js,.min.js}
npm run test:all     # unit tests -> build -> e2e. The full gate.

To try a change in a browser, serve the repository root — the examples load ../dist/TimeWidget.js, so serving example/ on its own gives you a 404:

npm run build
npx http-server . -p 8099 -c-1 -a 127.0.0.1
# then open http://localhost:8099/example/stocks.html

example/stocks.html is the most complete demo (real data, brushes, group medians, reference curves). npm run dev is a rollup watch build, not a server.

Working with an AI coding agent? CLAUDE.md documents the architecture, conventions, and the non-obvious traps in this codebase.

Testing

Two tiers, because they catch different things:

| Command | What it covers | |---|---| | npm test | Jest unit tests over the pure logic in src/utils.js, src/BVH.js | | npm run test:e2e | Playwright, real browser, driving the built bundle |

Put a test at the tier that can actually observe the bug. Pure-logic bug → unit test. Anything involving construction, rendering, the DOM, or two widgets on one page → e2e; those bugs are invisible to the unit suite, which has been fully green while exactly that class of bug was live.

Run npm run build before npm run test:e2e — the e2e suite drives dist/, so an un-built source change simply isn't there.

Prove a new test fails without its fix. Revert the fix, watch the test go red, then restore it. A test that has never failed proves nothing, and it is easy to write an assertion that quietly holds either way.

Both tiers run in CI on every pull request.

License

TimeWidget.js is licensed under the MIT license. (http://opensource.org/licenses/MIT)

Contributors

These research tools are the result of a collaboration between:

  • Universidad Rey Juan Carlos in Madrid (Iván Velasco, Sofía Bayona and Luis Pastor)
  • The Kangaroo Foundation in Colombia (Nathalie Charpak, José Tiberio Hernández)
  • Norhteastern University in Sillicon Valley (John Alexis Guerra Gomez)