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

tinyfft

v0.1.0

Published

Tiny no_std FFT (Cooley-Tukey radix-2) compiled to WebAssembly. 1D and 2D, ~11 KB wasm embedded as base64. Zero runtime dependencies.

Readme

tinyfft

CI npm

Tiny FFT for the browser and Node, written in no_std Rust and compiled to WebAssembly. ~11 KB wasm, embedded as base64 in the JS bundle. 1D and 2D, in-place. Zero runtime dependencies.

npm install tinyfft
import { TinyFft, interleave, magnitudes } from "tinyfft";

const fft = await TinyFft.load();   // wasm is bundled, no network fetch needed

// One-shot 1D
const real = Float32Array.from({ length: 16 }, (_, i) => Math.sin(i));
const spectrum = fft.forward(interleave(real));
const mags = magnitudes(spectrum);

// Hot-loop 1D (no per-call allocations)
fft.reset();
const plan = fft.plan1d(1024);
for (const frame of frames) {
  for (let i = 0; i < 1024; i++) {
    plan.view[2 * i]     = frame[i];
    plan.view[2 * i + 1] = 0;
  }
  plan.forward();
  // read plan.view (interleaved [re, im]) — same Float32Array, mutated in place
}

// 2D
fft.reset();
const p2 = fft.plan2d(256, 256);
// fill p2.view, then:
p2.forward();
// ... mask in frequency domain ...
p2.inverse();

API

| Symbol | Meaning | | ----------------------------------- | ---------------------------------------------------------------------------------------- | | TinyFft.load(source?) | Async factory. With no arg, uses bundled wasm. Pass a BufferSource, Response, or Promise<Response> to load from elsewhere. | | fft.plan1d(n)Plan1D | Allocates n complex samples in wasm memory. n must be a power of two. | | fft.plan2d(width, height)Plan2D | Allocates width × height plus an equally-sized scratch buffer. Both dims power of two. | | plan.view (Float32Array) | Interleaved [re, im, …] view directly over wasm memory. Read/write in place. | | plan.forward() / plan.inverse() | Run the transform. Inverse normalizes by 1/N (1D) or 1/(W·H) (2D). | | fft.forward(buf) / fft.inverse(buf) | One-shot 1D. Returns a fresh Float32Array (copies out of wasm memory). | | fft.forward2d(buf, w, h) / fft.inverse2d(...) | One-shot 2D. | | fft.reset() | Resets the wasm bump arena. Invalidates any existing plans/views. | | fft.arenaCapacity (number) | Total bytes available in the arena (8 MiB by default). | | interleave(real, imag?) | Helper: build [re, im, …] from real (and optional imag). | | magnitudes(buf) | Helper: per-bin |X[k]|. | | FftError | Thrown on wasm error codes (1 = not power of two, 2 = empty/null). |

Errors during plan creation come back as plain Error (e.g. arena exhaustion); errors from the wasm transform itself come back as FftError.

Memory model

The wasm module owns a fixed 8 MiB bump-allocated arena. Plans allocate from it. fft.reset() rewinds the bump pointer (cheap), invalidating every plan. There is no per-plan free — manage lifetimes by resetting between batches.

For larger workloads, increase ARENA_BYTES in src/lib.rs and rebuild.

Building from source

You need Rust with the wasm32-unknown-unknown target and Node ≥ 18.

rustup target add wasm32-unknown-unknown
npm install
npm run build      # cargo build + base64-embed wasm + tsc
npm test           # cargo test + smoke test

npm run build produces dist/:

dist/index.js          # ESM entry (~6.5 KB)
dist/index.d.ts        # types
dist/wasm-bytes.js     # auto-generated base64 string (~15 KB)

Total tarball published to npm: ~21 KB.

Examples

Two browser demos live under examples/. Both consume the package via the local build (../../dist/index.js); after publishing they would import "tinyfft" instead.

cd examples
npm install         # serve
npm run build       # builds the package one level up
npm run dev         # static server on :3000 rooted at the repo
# open http://localhost:3000/examples/spectrum-viewer/
# or   http://localhost:3000/examples/image-filter/

Releasing

CI on every PR runs cargo test, builds the wasm + ts, runs the smoke test, and verifies npm pack.

Tags v* trigger .github/workflows/release.yml, which rebuilds, verifies the tag matches package.json version, and runs npm publish --provenance --access public. Set the NPM_TOKEN secret in the repo for this to work.

npm version 0.1.1   # bumps package.json and creates a v0.1.1 tag
git push --follow-tags

Algorithm

Iterative Cooley–Tukey radix-2 over f32. Trig per stage via the recurrence w *= w_step (one cos/sin per stage, not per butterfly). 2D is row FFTs → out-of-place transpose → row FFTs → transpose back, reusing the 1D code. Inverse normalization is per-pass so 2D inverse is automatically 1/(W·H).

License

MIT