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

@pinta365/steganography

v0.3.2

Published

A steganography library supporting image and text steganography with LSB embedding, JPEG DCT coefficients, and zero-width character encoding.

Readme

@pinta365/steganography

A steganography library supporting image and text steganography.

Features

  • Image Steganography

    • LSB (Least Significant Bit) embedding for lossless formats
    • JPEG DCT coefficient-domain embedding
    • File embedding with metadata support
  • Text Steganography

    • Zero-Width Character (ZWC) encoding
    • Optional AES-256-CTR encryption
    • Compression support

Installation

JSR (Recommended)

# Deno
deno add jsr:@pinta365/steganography

# pnpm
pnpm i jsr:@pinta365/steganography

# Bun
bunx jsr add @pinta365/steganography

# Node.js (npx)
npx jsr add @pinta365/steganography

Or import directly:

import { decodeText, encodeText } from "jsr:@pinta365/[email protected]";

npm / Node.js / Bun

npm install @pinta365/steganography

Or with other package managers:

# yarn
yarn add @pinta365/steganography

# pnpm
pnpm add @pinta365/steganography

# bun
bun add @pinta365/steganography

Note: This library is runtime-agnostic and works on Deno, Node.js (18+), Bun, and browsers.

Usage

Text Steganography

import { decodeText, encodeText } from "@pinta365/steganography";

// Hide text in text
const coverText = "# Project README\n\nInstallation guide...";
const secret = "API key: sk_live_1234567890";
const stegaText = await encodeText(coverText, secret, "password", true);
const { visibleText, secretMessage } = await decodeText(stegaText, "password");

Image Steganography

import { createImage, decodeImage, embedTextInImage, encodeImage, extractTextFromImage } from "@pinta365/steganography";

// Load image
const imageData = await Deno.readFile("image.png");
const image = await decodeImage(imageData);

// Embed message
const message = "Hidden secret message";
const modifiedData = embedTextInImage(image.data, message);

// Save image with hidden data
const stegaImage = createImage(image.width, image.height, modifiedData);
const output = await encodeImage(stegaImage, "png");
await Deno.writeFile("stega.png", output);

// Extract message
const loadedImage = await decodeImage(await Deno.readFile("stega.png"));
const extractedMessage = extractTextFromImage(loadedImage.data);
console.log(extractedMessage); // "Hidden secret message"

Animated Image Steganography

import { decodeImageFrames, embedTextInImageFrames, encodeImageFrames, extractTextFromImageFrames } from "@pinta365/steganography";

// Load animated GIF
const gifData = await Deno.readFile("animated.gif");
const multiFrame = await decodeImageFrames(gifData, "gif");

// Embed message in first frame
const message = "Hidden message";
const stegaMultiFrame = embedTextInImageFrames(multiFrame, message, 1, "first");

// Save as GIF
const stegaGifData = await encodeImageFrames("gif", stegaMultiFrame);
await Deno.writeFile("stega.gif", stegaGifData);

// Extract message
const decoded = await decodeImageFrames(stegaGifData, "gif");
const extracted = extractTextFromImageFrames(decoded);
console.log(extracted); // "Hidden message"

Embedding modes:

  • "first" - Embed only in the first frame (recommended for most cases)
  • "all" - Embed the same message in all frames
  • "split" - Distribute message across frames (useful for large messages)
## Capacity and Configuration

### Capacity Calculation

The library automatically calculates capacity based on your cover media:

- **Text Steganography**: Capacity depends on cover text length. Each byte requires ~4 zero-width characters. Use `calculateTextCapacity(coverText)`
  to check capacity.
- **Image Steganography**: Capacity depends on image dimensions and bit depth. Use `calculateBitCapacity(width, height, bitDepth)` to check capacity.

### Configurable Limits

All encoding functions support optional configuration via options:

**Text Functions** (`encodeText`, `encodeBinary`):

```typescript
interface EncodeOptions {
    maxPayloadBytes?: number; // Override calculated capacity
    maxSecretLength?: number; // Max secret size (default: 50KB)
    maxCoverLength?: number; // Max cover text size (default: 100KB)
    strictCapacity?: boolean; // Throw error vs warn (default: true)
}

Image Functions (embedTextInImage, embedDataInImage):

interface ImageEncodeOptions {
    maxPayloadBytes?: number; // Override calculated capacity
    maxMessageLength?: number; // Max message size (default: 10MB)
    strictCapacity?: boolean; // Throw error vs warn (default: true)
}

Capacity Warnings

By default, the library will throw errors if payload exceeds capacity. Set strictCapacity: false to get warnings instead:

// Warns but continues encoding
const stegaText = await encodeText(cover, secret, undefined, false, {
    strictCapacity: false,
});

Examples

// Check capacity before encoding
const capacity = calculateTextCapacity(coverText);
console.log(`Can hide up to ${capacity} bytes`);

// Override capacity limit
const stegaText = await encodeText(coverText, secret, undefined, false, {
    maxPayloadBytes: 10000, // Allow up to 10KB
    maxSecretLength: 20000, // Allow up to 20KB secret
});

// Use calculated capacity with warnings
const stegaImage = embedTextInImage(image.data, message, 1, {
    strictCapacity: false, // Warn instead of error
});

API Reference

Types

StegaText

Branded type for steganographic text containing hidden ZWC data. Returned by encoding functions and accepted by decoding functions for type safety.

type StegaText = string & { readonly __brand: "StegaText" };

Image

Image type from @cross/image, re-exported for convenience. Use for type annotations when working with image helper functions.

import { decodeImage, Image } from "@pinta365/steganography";

const image: Image = await decodeImage(imageData);

Text Steganography

encodeText(coverText, secretMessage, password?, distribute?)

Encodes a secret text message into cover text using zero-width characters.

  • coverText (string): Visible text that will contain the hidden message
  • secretMessage (string): Secret text to hide
  • password (string?): Optional password for AES-256-CTR encryption
  • distribute (boolean?): If true, distributes ZWC characters throughout text (default: false)
  • Returns: Promise<StegaText> - Cover text with invisible ZWC payload embedded

decodeText(stegaText, password?)

Decodes a hidden text message from text with hidden data.

  • stegaText (string | StegaText): Text containing hidden ZWC data
  • password (string?): Password if the message was encrypted
  • Returns: Promise<{ visibleText: string; secretMessage: string | null }>

encodeBinary(coverText, binaryData, password?, distribute?)

Encodes binary data (images, files) into cover text using zero-width characters.

  • coverText (string): Visible text that will contain the hidden data
  • binaryData (Uint8Array): Binary data to hide
  • password (string?): Optional password for AES-256-CTR encryption
  • distribute (boolean?): If true, distributes ZWC characters throughout text (default: false)
  • Returns: Promise<StegaText> - Cover text with invisible ZWC payload embedded

decodeBinary(stegaText, password?)

Decodes binary data from text with hidden data.

  • stegaText (string | StegaText): Text containing hidden ZWC data
  • password (string?): Password if the data was encrypted
  • Returns: Promise<{ visibleText: string; binaryData: Uint8Array | null }>

decode(stegaText, password?)

Unified decode function that auto-detects payload type (text or binary).

  • stegaText (string | StegaText): Text containing hidden ZWC data
  • password (string?): Password if the data was encrypted
  • Returns: Promise<{ visibleText: string; payloadType: "text" | "binary" | null; textData: string | null; binaryData: Uint8Array | null }>

hasHiddenData(text)

Checks if text contains hidden ZWC steganography data.

  • text (string | StegaText): Text to check
  • Returns: boolean

analyzeZWC(text)

Returns statistics about hidden data in text.

  • text (string): Text to analyze
  • Returns: ZWCStats - Object with hasHiddenData, visibleLength, zwcCount, estimatedPayloadBytes, breakdown

stripZWC(text)

Removes all ZWC characters from text (removes any hidden data).

  • text (string): Text to clean
  • Returns: string

Image Utilities

decodeImage(imageData)

Decodes image file data into an Image object.

  • imageData (Uint8Array): Image file data (PNG, JPEG, WebP, etc.)
  • Returns: Promise<Image> - Image object with width, height, and RGBA data

encodeImage(image, format, options?)

Encodes an Image object to file data.

  • image (Image): Image object
  • format (string): Output format (png, jpeg, webp, etc.)
  • options (ImageFormatEncodeOptions?): Optional encoding options (format-specific). See @cross/image documentation for format-specific option types:
    • PNG/APNG: PNGEncoderOptions (compressionLevel: 0-9, default: 6)
    • WebP: WebPEncoderOptions (quality: 1-100, lossless: boolean, default: quality: 90)
    • TIFF: TIFFEncoderOptions (compression: "none" | "lzw" | "packbits" | "deflate", default: "lzw")
    • Other formats: See @cross/image types
  • Returns: Promise<Uint8Array> - Encoded image file data

createImage(width, height, data)

Creates a new Image object.

  • width (number): Image width in pixels
  • height (number): Image height in pixels
  • data (Uint8Array): RGBA image data
  • Returns: Image - Image object

Image Steganography (LSB)

LSB (Least Significant Bit) steganography embeds data in the least significant bits of image pixels. Works with lossless formats (PNG, WebP lossless, BMP, etc.). Lossy formats (JPEG) will destroy hidden data on re-encoding.

embedTextInImage(imageData, message, bitDepth?, options?)

Embeds a text message into image data using LSB (Least Significant Bit).

  • imageData (Uint8Array): RGBA image data
  • message (string): Text message to embed
  • bitDepth (number?): Bits per pixel (1-4, default: 1)
  • options (ImageEncodeOptions?): Optional encoding options (capacity limits, strict mode)
  • Returns: Uint8Array - Modified image data

extractTextFromImage(imageData, bitDepth?)

Extracts a text message from image data using LSB. The message length is automatically read from the embedded header.

  • imageData (Uint8Array): RGBA image data
  • bitDepth (number?): Bits per pixel (1-4, default: 1)
  • Returns: string - Extracted text message

embedDataInImage(imageData, data, bitDepth?, options?)

Embeds binary data into image data using LSB.

  • imageData (Uint8Array): RGBA image data
  • data (Uint8Array): Binary data to embed
  • bitDepth (number?): Bits per pixel (1-4, default: 1)
  • options (ImageEncodeOptions?): Optional encoding options (capacity limits, strict mode)
  • Returns: Uint8Array - Modified image data

extractDataFromImage(imageData, dataLength, bitDepth?)

Extracts binary data from image data using LSB.

  • imageData (Uint8Array): RGBA image data
  • dataLength (number): Length of the data in bytes
  • bitDepth (number?): Bits per pixel (1-4, default: 1)
  • Returns: Uint8Array - Extracted binary data

embedLSB(imageData, messageBits, bitDepth?)

Low-level function to embed bits into image pixels.

  • imageData (Uint8Array): RGBA image data
  • messageBits (Uint8Array): Bits to embed
  • bitDepth (number?): Bits per pixel (1-4, default: 1)
  • Returns: Uint8Array - Modified image data

extractLSB(imageData, bitCount, bitDepth?)

Low-level function to extract bits from image pixels.

  • imageData (Uint8Array): RGBA image data
  • bitCount (number): Number of bits to extract
  • bitDepth (number?): Bits per pixel (1-4, default: 1)
  • Returns: Uint8Array - Extracted bits

calculateBitCapacity(width, height, bitDepth?)

Calculates the bit capacity of an image.

  • width (number): Image width in pixels
  • height (number): Image height in pixels
  • bitDepth (number?): Bits per pixel (1-4, default: 1)
  • Returns: number - Number of bytes that can be hidden

generateLSBStats(imageData, originalData?)

Generates LSB statistics for display.

  • imageData (Uint8Array): RGBA image data
  • originalData (Uint8Array?): Original image data for comparison
  • Returns: Object with LSB statistics per channel (red, green, blue, total)

JPEG Coefficient Steganography

embedDataInJpegCoefficients(coefficients, data, useChroma?)

Embeds binary data into JPEG coefficients using DCT coefficient-domain embedding.

  • coefficients (JPEGQuantizedCoefficients): JPEG quantized coefficients (will be modified)
  • data (Uint8Array): Binary data to embed
  • useChroma (boolean?): Use chroma components (default: true)
  • Returns: JPEGQuantizedCoefficients - Modified coefficients

extractDataFromJpegCoefficients(coefficients, maxBytes, useChroma?)

Extracts binary data from JPEG coefficients.

  • coefficients (JPEGQuantizedCoefficients): JPEG quantized coefficients
  • maxBytes (number): Maximum number of bytes to extract
  • useChroma (boolean?): Extract from chroma components (default: true)
  • Returns: Uint8Array - Extracted binary data

embedInCoefficients(coefficients, messageBits, useChroma?)

Low-level function to embed bits into JPEG coefficients.

  • coefficients (JPEGQuantizedCoefficients): JPEG quantized coefficients (will be modified)
  • messageBits (Uint8Array): Bits to embed
  • useChroma (boolean?): Use chroma components (default: true)
  • Returns: JPEGQuantizedCoefficients - Modified coefficients

extractFromCoefficients(coefficients, bitCount, useChroma?)

Low-level function to extract bits from JPEG coefficients.

  • coefficients (JPEGQuantizedCoefficients): JPEG quantized coefficients
  • bitCount (number): Number of bits to extract
  • useChroma (boolean?): Extract from chroma components (default: true)
  • Returns: Uint8Array - Extracted bits

calculateJpegCoefficientCapacity(coefficients, useChroma?)

Calculates the embedding capacity of JPEG coefficients.

  • coefficients (JPEGQuantizedCoefficients): JPEG quantized coefficients
  • useChroma (boolean?): Include chroma components (default: true)
  • Returns: number - Number of bytes that can be hidden

extractJpegCoefficients(jpegData)

Extracts JPEG quantized coefficients from JPEG data.

  • jpegData (Uint8Array): JPEG file data
  • Returns: Promise<JPEGQuantizedCoefficients | null>

encodeJpegFromCoefficients(coefficients)

Encodes JPEG from quantized coefficients.

  • coefficients (JPEGQuantizedCoefficients): JPEG quantized coefficients
  • Returns: Promise<Uint8Array> - JPEG file data

cloneJpegCoefficients(coefficients)

Deep clones JPEG coefficients to avoid modifying the original.

  • coefficients (JPEGQuantizedCoefficients): JPEG quantized coefficients
  • Returns: JPEGQuantizedCoefficients - Cloned coefficients

Utility Functions

bytesToBits(bytes)

Converts a byte array to a bit array (LSB first).

  • bytes (Uint8Array): Bytes to convert
  • Returns: Uint8Array - Bit array

bitsToBytes(bits)

Converts a bit array back to bytes (LSB first).

  • bits (Uint8Array): Bits to convert
  • Returns: Uint8Array - Byte array

xorEncrypt(data, password)

XOR encrypts data using a cyclic password key.

  • data (Uint8Array): Data to encrypt
  • password (string): Password key
  • Returns: Uint8Array - Encrypted data

xorDecrypt(data, password)

XOR decrypts data (XOR is its own inverse).

  • data (Uint8Array): Data to decrypt
  • password (string): Password key
  • Returns: Uint8Array - Decrypted data

detectImageFormat(data)

Detects image format from file data.

  • data (Uint8Array): Image file data
  • Returns: string | null - Format name or null if unknown

isLossyFormat(format)

Checks if a format is lossy (will destroy pixel-domain embedding data on re-encoding).

  • format (string | null): Format name
  • Returns: boolean

getRecommendedOutputFormat(inputFormat)

Gets a recommended output format based on input format.

  • inputFormat (string | null): Input format name
  • Returns: Object with format, reason, and optional useWebP

License

MIT