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

@arijs/transfer-speed-graph

v0.1.3

Published

A lightweight, modular transfer speed graph inspired by the Windows 8 file transfer dialog, with support for: Real transfer integration (download/upload), Deterministic and random fake transfer simulation, Pause/cancel/finish states, and Highly configurab

Downloads

722

Readme

Transfer Speed Graph

Transfer Speed Graph

Transfer Speed Graph homepage / NPM Package NPM Version

A lightweight, modular transfer-speed graph inspired by the Windows 8 file transfer dialog, with support for:

  • Real transfer integration (download/upload)
  • Deterministic and random fake transfer simulation
  • Pause/cancel/finish states
  • Highly configurable graph smoothing and dynamic Y-scale behavior

Real Example

Deterministic transfer at 50%

Quick Usage

Install

npm install @arijs/transfer-speed-graph

Then use the object:

import { TransferGraph } from '@arijs/transfer-speed-graph'

const graph = new TransferGraph()

CDN

If you prefer not to use a bundler, you can use either ES module imports or the UMD bundle.

ES module import via jsDelivr:

import { TransferGraph } from 'https://cdn.jsdelivr.net/npm/@arijs/transfer-speed-graph@latest/+esm'

const graph = new TransferGraph()

ES module import via unpkg:

import { TransferGraph } from 'https://unpkg.com/@arijs/transfer-speed-graph@latest?module'

const graph = new TransferGraph()

UMD script via jsDelivr:

<script src="https://cdn.jsdelivr.net/npm/@arijs/transfer-speed-graph@latest/dist/arijs-transfer-speed-graph.umd.js"></script>

UMD script via unpkg:

<script src="https://unpkg.com/@arijs/transfer-speed-graph@latest/dist/arijs-transfer-speed-graph.umd.js"></script>

HTML Script Tag

After loading one of the CDN scripts above, you can initialize the graph with a minimal HTML snippet:

<canvas id="graph" width="416" height="72"></canvas>
<script>
  const graph = new TransferSpeedGraph.TransferGraph()
  const canvasCtx = document.getElementById('graph').getContext('2d')

  graph.setRendererOptions({
    canvasCtx,
    canvasWidth: 416,
    canvasHeight: 72,
  })

  graph.startTransfer({ totalSize: 12 * 1024 * 1024 })
</script>

Download Example

import { TransferGraph } from '@arijs/transfer-speed-graph'

const controller = new TransferGraph()
controller.setOnFrame((view) => {
  // update your UI with view.progressInt, view.speedBps, etc.
})

const downloadUrl = 'https://httpbin.org/bytes/12582912?seed=2026'
const fallbackTotalSize = 12 * 1024 * 1024

controller.setRendererOptions({
  canvasCtx,
  canvasWidth: 416,
  canvasHeight: 72,
})

fetch(downloadUrl).then(async (res) => {
  if (!res.ok || !res.body) throw new Error('Download failed: ' + res.status)

  const totalHeader = parseInt(res.headers.get('content-length') || '0', 10)
  const totalSize = Number.isFinite(totalHeader) && totalHeader > 0
    ? totalHeader
    : fallbackTotalSize

  const reader = res.body.getReader()
  let transferredBytes = 0

  controller.startTransfer({ totalSize })

  while (true) {
    const chunk = await reader.read()
    if (chunk.done) break
    transferredBytes += chunk.value.byteLength
    controller.pushProgress({ transferredBytes, totalSize })
  }

  controller.finishTransfer({ transferredBytes, totalSize })
}).catch((err) => {
  console.warn(err)
  controller.cancel()
})

Source example: real/download-example.js

Upload Example

import { TransferGraph } from '@arijs/transfer-speed-graph'

const controller = new TransferGraph()
const uploadUrl = 'https://httpbin.org/post'
const uploadSize = 10 * 1024 * 1024

const xhr = new XMLHttpRequest()
const payload = new Blob([new Uint8Array(uploadSize)])

controller.setRendererOptions({
  canvasCtx,
  canvasWidth: 416,
  canvasHeight: 72,
})
controller.startTransfer({ totalSize: uploadSize })

xhr.upload.addEventListener('progress', (ev) => {
  if (!ev.lengthComputable) return
  controller.pushProgress({
    transferredBytes: ev.loaded,
    totalSize: ev.total,
  })
})

xhr.addEventListener('load', () => {
  controller.finishTransfer({
    transferredBytes: uploadSize,
    totalSize: uploadSize,
  })
})

xhr.addEventListener('error', () => {
  console.warn('upload failed')
  controller.cancel()
})

xhr.addEventListener('abort', () => {
  controller.cancel()
})

xhr.open('POST', uploadUrl)
xhr.send(payload)

Source example: real/upload-example.js

State Showcase

Paused state:

Paused transfer state

Cancelled state:

Cancelled transfer state

Why This Library Is Strong

Source Files

The published package is split by concern:

  • Graph core:
    • core/main.js public TransferGraph facade that coordinates model, renderer, and runtime APIs.
    • core/model.js transfer state machine plus series, timing, and control state management.
    • core/renderer.js renderer configuration layer that resolves options and delegates frame drawing.
    • core/frame.js canvas frame rendering, graph geometry, and draw-hook execution.
    • core/speed-series.js series utilities for averaging, speed calculations, and coordinate transforms.
  • Fake transfer subsystem:
  • Real transfer examples:

That separation keeps rendering, state management, fake generation, and transport examples independent and easy to evolve.

High Configurability

Graph behavior can be tuned at runtime with controls such as:

  • pixelAverageWindow
  • maxSpeedDecay
  • maxSpeedHeadroom
  • ignoreTrailingSpeedSample

Fake simulation is configurable through simulationOptions, including:

  • minFrame, maxFrame
  • minFrameRepeat, maxFrameRepeat
  • minSizeInc, maxSizeInc
  • deterministicSeed

This allows both realistic noisy profiles and stable deterministic test profiles.

Practical Runtime API

TransferGraph gives a straightforward app-facing API:

  • startTransfer

    Start a new transfer and reset the runtime state.

    Signature:

    startTransfer(config)
    • config.totalSize: required positive total size for the transfer.
    • config.nowMs: optional timestamp used as the transfer start time.

    Example:

    const graph = new TransferGraph()
    graph.startTransfer({ totalSize: 12 * 1024 * 1024 })
  • pushProgress

    Append a new progress update to the active transfer.

    Signature:

    pushProgress(update)
    • update.transferredBytes: current transferred byte count.
    • update.totalSize: optional total size override.
    • update.elapsedMs: optional elapsed time override.
    • update.nowMs: optional timestamp used to resolve elapsed time.

    Example:

    const graph = new TransferGraph()
    graph.pushProgress({ transferredBytes: 4 * 1024 * 1024 })
  • finishTransfer

    Mark the transfer as finished and capture the final point.

    Signature:

    finishTransfer(update)
    • update.transferredBytes: final transferred byte count.
    • update.totalSize: optional total size override.
    • update.elapsedMs: optional elapsed time override.
    • update.nowMs: optional timestamp used to resolve elapsed time.

    Example:

    const graph = new TransferGraph()
    graph.finishTransfer({ transferredBytes: 12 * 1024 * 1024 })
  • cancel

    Cancel the active transfer and freeze it in a finished state.

    Signature:

    cancel()

    Example:

    const graph = new TransferGraph()
    graph.cancel()
  • reset

    Reset the model back to its initial state.

    Signature:

    reset()

    Example:

    const graph = new TransferGraph()
    graph.reset()
  • pause

    Pause elapsed-time tracking without clearing transfer progress.

    Signature:

    pause(nowMs)
    • nowMs: optional timestamp used to freeze elapsed-time calculations.

    Example:

    const graph = new TransferGraph()
    graph.pause()
  • resume

    Resume elapsed-time tracking after a pause.

    Signature:

    resume(nowMs)
    • nowMs: optional timestamp used to resume elapsed-time calculations.

    Example:

    const graph = new TransferGraph()
    graph.resume()
  • toggleFinishedPauseVisual

    Toggle the paused visual state that is shown after a transfer finishes.

    Signature:

    toggleFinishedPauseVisual()

    Example:

    const graph = new TransferGraph()
    graph.toggleFinishedPauseVisual()
  • refreshGraphScale

    Force the renderer to recalculate the speed scale on the next frame.

    Signature:

    refreshGraphScale()

    Example:

    const graph = new TransferGraph()
    graph.refreshGraphScale()
  • setPixelAverageWindow

    Change the rolling average window used by the renderer.

    Signature:

    setPixelAverageWindow(nextWindow)
    • nextWindow: smoothing window size in rendered points.

    Example:

    const graph = new TransferGraph()
    graph.setPixelAverageWindow(16)
  • setMaxSpeedDecay

    Change the decay factor used for the dynamic speed scale.

    Signature:

    setMaxSpeedDecay(nextValue)
    • nextValue: new decay value, typically between 0.5 and 0.999.

    Example:

    const graph = new TransferGraph()
    graph.setMaxSpeedDecay(0.95)
  • setMaxSpeedHeadroom

    Change the headroom factor used for the dynamic speed scale.

    Signature:

    setMaxSpeedHeadroom(nextValue)
    • nextValue: new headroom value, typically between 1 and 2.

    Example:

    const graph = new TransferGraph()
    graph.setMaxSpeedHeadroom(1.08)
  • setOnFrame

    Register a callback that receives the computed frame view model.

    Signature:

    setOnFrame(onFrame)
    • onFrame(view): receives the computed frame view model.

    Example:

    const graph = new TransferGraph()
    graph.setOnFrame((view) => {
    console.log(view.progressInt)
    })
  • setOnControls

    Register a callback that receives the current controls state.

    Signature:

    setOnControls(onControls)
    • onControls(view): receives the current controls state.

    Example:

    const graph = new TransferGraph()
    graph.setOnControls((controls) => {
    console.log(controls.pixelAverageWindow)
    })
  • setOnStateChange

    Register a callback that receives the raw state snapshot.

    Signature:

    setOnStateChange(onStateChange)
    • onStateChange(state): receives the raw state snapshot.

    Example:

    const graph = new TransferGraph()
    graph.setOnStateChange((state) => {
    console.log(state.started, state.finished)
    })

Canvas Drawing Hooks

TransferGraphRenderer accepts optional draw hooks that let you customize the canvas output without replacing the whole renderer. If a hook is omitted, the default implementation reproduces the current visuals.

Provide them through new TransferGraph({ ... }) or setRendererOptions({ ... }):

Common argument properties

Every draw hook receives these shared properties:

  • canvasCtx: the active 2D canvas context for the current frame.

  • canvasWidth: the canvas width in pixels.

  • canvasHeight: the canvas height in pixels.

  • drawProgressBar

    Signature:

    drawProgressBar({ canvasCtx, canvasWidth, canvasHeight, lastX, backgroundValue, createDefaultPath })
    • lastX: the resolved filled width of the progress bar in canvas coordinates.
    • backgroundValue: the value used to compute the background fill width.
    • createDefaultPath(): builds the current progress-bar path with beginPath() + rect().

    Default: the renderer calls createDefaultPath(), then fill() and stroke().

    const graph = new TransferGraph({
    drawProgressBar({ canvasCtx, createDefaultPath }) {
      createDefaultPath()
      canvasCtx.fill()
      canvasCtx.stroke()
    },
    })
  • drawGrid

    Signature:

    drawGrid({ canvasCtx, canvasWidth, canvasHeight, gridCols, gridRows, createDefaultPath })
    • gridCols: number of vertical grid divisions.
    • gridRows: number of horizontal grid divisions.
    • createDefaultPath(): builds the current grid paths for both directions.

    Default: the renderer calls createDefaultPath(), then stroke().

    const graph = new TransferGraph({
    drawGrid({ canvasCtx, createDefaultPath }) {
      createDefaultPath()
      canvasCtx.stroke()
    },
    })
  • drawSpeedOverlay

    Signature:

    drawSpeedOverlay({ canvasCtx, canvasWidth, canvasHeight, lastX, endX, avgWithSpeeds, renderPointCount, pixelsPerValue, maxAvgSpeed, createDefaultPath })
    • lastX: the resolved filled width of the progress area.
    • endX: the right edge where the overlay path should continue until the filled progress ends.
    • avgWithSpeeds: the calculated speed points used to draw the overlay.
    • renderPointCount: number of points used when rendering the visible speed curve.
    • pixelsPerValue: the conversion factor from value units to canvas X pixels.
    • maxAvgSpeed: the resolved speed scale used to normalize the overlay height.
    • createDefaultPath(): builds the current speed-area path and lets the default implementation resolve the current point position.

    Default: the renderer calls createDefaultPath(), then fill().

    const graph = new TransferGraph({
    drawSpeedOverlay({ canvasCtx, createDefaultPath }) {
      createDefaultPath()
      canvasCtx.fill()
    },
    })
  • drawSpeedLineLabel

    Signature:

    drawSpeedLineLabel({ canvasCtx, canvasWidth, canvasHeight, guideY, textX, labelBottomY, labelTopY, labelLeftX, labelWidth, labelPaddingX, labelPaddingY, speedLabel, speedLabelColor, speedLabelBackgroundColor, speedGuideColor, createDefaultLinePath, createDefaultLabelBackgroundPath, fillDefaultLabelText })
    • guideY: the Y position of the horizontal guide line.
    • textX: the right-aligned X position used for the label text.
    • labelBottomY: the baseline Y position for the label text.
    • labelTopY: the top Y position of the background box.
    • labelLeftX: the left X position of the background box.
    • labelWidth: measured width of the label text.
    • labelPaddingX: horizontal padding used by the default label box.
    • labelPaddingY: vertical padding used by the default label box.
    • speedLabel: the formatted label string.
    • speedLabelColor: the current text color.
    • speedLabelBackgroundColor: the current background fill color.
    • speedGuideColor: the current guide-line stroke color.
    • createDefaultLinePath(): builds the current guide-line path.
    • createDefaultLabelBackgroundPath(): builds the current label background path with beginPath() + rect().
    • fillDefaultLabelText(options): draws the current text using fillText(). Optional options can override fill style and text alignment for custom drawing.

    Default: the renderer strokes the line path, fills the background path, and calls fillDefaultLabelText().

    const graph = new TransferGraph({
    drawSpeedLineLabel({
      canvasCtx,
      createDefaultLinePath,
      createDefaultLabelBackgroundPath,
      fillDefaultLabelText,
    }) {
      createDefaultLinePath()
      canvasCtx.stroke()
      createDefaultLabelBackgroundPath()
      canvasCtx.fill()
      fillDefaultLabelText()
    },
    })
  • drawBorder

    Signature:

    drawBorder({ canvasCtx, canvasWidth, canvasHeight, borderColor, createDefaultPath })
    • borderColor: the current border stroke color.
    • createDefaultPath(): builds the current canvas border path with beginPath() + rect().

    Default: the renderer calls createDefaultPath(), then stroke().

    const graph = new TransferGraph({
    drawBorder({ canvasCtx, createDefaultPath }) {
      createDefaultPath()
      canvasCtx.stroke()
    },
    })

Development

See repo at https://github.com/rhengles/transfer-speed-graph