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

@kenbarbour/nodem

v1.0.0

Published

A JavaScript library for encoding and decoding AX.25 packet radio frames as AFSK audio, for web and Node.js applications.

Readme

nodem

A pure JavaScript library for encoding and decoding AX.25 packet radio frames as AFSK audio. Runs in browsers and Node.js with no build step and no dependencies.

AX.25 is the Layer 2 protocol used by amateur radio packet networks including APRS. nodem converts between AX.25 frame objects and Float32Array audio samples, making it straightforward to transmit and receive packet radio signals via a sound card or Web Audio API.

Why nodem?

  • Pure JavaScript — no native addons, no compile step, no dependencies
  • Works everywhere — Node.js and browsers via the Web Audio API
  • Fast — decoding speed is comparable to Direwolf, which is written in C
  • Accurate — decodes 911 of 1000 packets on TNC Test CD Track 2 (Direwolf decodes 983)
  • Faithful — produces nearly identical waveforms to Direwolf with the same inputs
  • Tested — 100% test coverage

Installation

npm install @kenbarbour/nodem

Usage

import { encoder, decoder } from '@kenbarbour/nodem';

// All parameters are optional — these are the defaults.
const config = {
  sampleRate:     44100,  // Hz, standard for Web Audio API
  baudRate:       1200,   // symbols/second
  markFreq:       1200,   // Hz — binary 1 tone
  spaceFreq:      2200,   // Hz — binary 0 tone
  preambleFlags:  4,      // AX.25 flag bytes before each frame
  postambleFlags: 4,      // AX.25 flag bytes after each frame
  
  // packet callback used by decoder
  onPacket: (packet) => console.log(packet), // default no-op
};

// Encoder: packet object → Float32Array of audio samples
const encode = encoder(config);

const samples = encode({
  source: { callsign: 'N0CALL', ssid: 0 },
  dest:   { callsign: 'APRS',   ssid: 0 },
  frame:  { type: 'U', subtype: 'UI' },
  data:   'Hello, World!',
});
// samples is a Float32Array at config.sampleRate Hz.
// Feed it to the Web Audio API, write it to a WAV file, etc.

// Decoder: streams Float32Array chunks → packets via onPacket callback
const decode = decoder(config);

// Call repeatedly as audio arrives — safe to chunk at any boundary.
decode(float32ArrayChunk);

Packet structure

| Field | Type | Notes | | ------- | ------ | ------- | | source | { callsign: string, ssid: number } | Originating station | | dest | { callsign: string, ssid: number } | Destination address | | digipeaters | Array<{ callsign, ssid }> | Optional relay path | | frame | { type, subtype } | type: 'U'/'I'/'S'; subtype e.g. 'UI' | | data | string \| Uint8Array | Encoder accepts string; decoder returns Uint8Array |

packet.data from the decoder is a Uint8Array. Decode it as UTF-8 text with new TextDecoder().decode(packet.data).

ssid in encoder input accepts either a plain number (ssid: 3) or the decoder's detail object (ssid: { ssid: 3, isCommand: true, ... }), so decoded packets can be re-encoded directly.

Configuration reference

Common (encoder and decoder):

| Key | Default | Description | | ----- | --------- | ------------- | | sampleRate | 44100 | Audio sample rate (Hz) | | baudRate | 1200 | Symbol rate (baud) | | markFreq | 1200 | Mark tone frequency (Hz) — binary 1 | | spaceFreq | 2200 | Space tone frequency (Hz) — binary 0 |

Encoder:

| Key | Default | Description | | ----- | --------- | ------------- | | preambleFlags | 4 | AX.25 flag bytes prepended to each frame | | postambleFlags | 4 | AX.25 flag bytes appended to each frame |

Decoder:

| Key | Default | Description | |-----|---------|-------------| | onPacket | () => {} | Called with each successfully decoded packet | | onError | () => {} | Called with any Error thrown while decoding a frame that passed FCS (e.g. unknown frame type). Defaults to a no-op — omitting this handler will silently discard decode errors. | | windowFn | hann | Window function (i, N) => number applied during frequency analysis. Built-ins (hann, hamming, blackman, nuttall, rectangular) are exported from @kenbarbour/nodem/windows, or supply your own. |

Performance

Benchmarked against the TNC Test CD with default settings:

| Track | nodem | Direwolf | | ------- | ------- | ---------- | | Track 1 | 986 packets | 1005 packets | | Track 2 | 911 packets | 983 packets |

Direwolf is written in C. On the same hardware both run at roughly 85–88× realtime, making nodem competitive in throughput despite being pure JavaScript.

Scripts

The package includes three CLI scripts for quick experimentation:

# Encode a test packet to test.wav
npx nodem-encode

# Decode a WAV file and print packets
npx nodem-decode path/to/file.wav

# Tune decoder parameters against one or more WAV files
npx nodem-tune track1.wav track2.wav

License

This software is licensed under the MIT license.