Stega64 encodes and decodes messages in image pixels.
Stega64.encode({ source: HTMLImageElement | HTMLCanvasElement;
messages: string[]; encoding: Stega64Encoding; encodeMetadata:
boolean; minWidth?: number; minHeight?: number; borderWidth?:
number; }): HTMLCanvasElement
const source = await loadImageFromImageUrl({
url: "./example.jpg"
});
Stega64.encode({
source,
encoding: "",
encodeMetadata: ,
messages: [""],
minWidth: ,
minHeight: ,
borderWidth: ,
aspectRatio:
})

Stega64.decode({ source: HTMLImageElement | HTMLCanvasElement;
encoding: Stega64Encoding; borderWidth?: number; }): String
const result = Stega64.decode({ source });
StegaCassette encodes and decodes audio in image pixels.
StegaCassette.encode({ source?: HTMLImageElement |
HTMLCanvasElement; sources?: (HTMLImageElement |
HTMLCanvasElement)[]; audioBuffers: Float32Array[]; sampleRate:
number; bitDepth: StegaCassetteBitDepth; encoding:
StegaCassetteEncoding; encodeMetadata?: boolean; aspectRatio?:
number; borderWidth?: number; }): HTMLCanvasElement |
HTMLCanvasElement[]
const audioBuffers = await loadAudioBuffersFromAudioUrl({
url: "./example.mp3",
audioContext,
channels: ,
sampleRate: ,
});
const result = StegaCassette.encode({
source,
audioBuffers,
sampleRate: ,
bitDepth: ,
encoding: "",
encodeMetadata: ,
aspectRatio: ,
borderWidth:
});

StegaCassette.decode({ source?: HTMLImageElement |
HTMLCanvasElement; sources?: (HTMLImageElement |
HTMLCanvasElement)[]; bitDepth: StegaCassetteBitDepth; channels:
StegaCassetteChannels; encoding: StegaCassetteEncoding;
borderWidth?: number; }): Float32Array[]
const metadata = StegaMetadata.decode({ source }) || {};
...
const audioBuffers = StegaCassette.decode({
source,
bitDepth: metadata.bitDepth || ,
channels: metadata.channels || ,
encoding: metadata.encoding || "",
});
const audio = await playDecodedAudioBuffers({
audioBuffers,
audioContext,
sampleRate: metadata.sampleRate || ,
});
Splitting audio across multiple images
StegaCassette can split audio data across multiple images
using the sources parameter for encoding and decoding. To
properly play back the audio, all images must be provided in the right
order. They can still be heard when provided partially (will sound
sped up depending on how many samples are missing) or in the wrong
order (will sound distored).
const result = StegaCassette.encode({
,
audioBuffers,
sampleRate: ,
bitDepth: ,
encoding: "",
});
const audioBuffers = StegaCassette.decode({
,
bitDepth: ,
channels: ,
encoding: "",
});
Obfuscating the data
StegaCassette encodes audio with a
key parameter to apply permutation-based encryption,
making the audio unplayable without the correct key. Keys can be
strings or StegaMetadataString images. You can use
Stega64 to create these.
const result = StegaCassette.encode({
source,
audioBuffers,
sampleRate: ,
bitDepth: ,
encoding: "",
key:
});
const audioBuffers = StegaCassette.decode({
source,
bitDepth: ,
channels: ,
encoding: "",
key:
});
Encoding audio waveforms in image pixels.
This is a demonstration of how waveforms are stored inside of pixels
using StegaCassette.
StegaMetadata can be optionally encoded inside of stega images.
StegaMetadata.encode({ source: HTMLCanvasElement; metadata:
StegaMetadata }): HTMLCanvasElement
StegaMetadata.decode({ source: HTMLCanvasElement; }): StegaMetadata
| null
enum StegaContentType StegaContentType {
AUDIO = 0,
STRING = 1,
ROBUST = 2,
MUSIC = 3,
BINARY = 4,
KEY = 5,
}
type StegaMetadata =
| StegaMetadataAudio
| StegaMetadataString
| StegaMetadataRobust
| StegaMetadataMusic
| StegaMetadataBinary
| StegaMetadataKey;
interface StegaMetadataAudio {
type: StegaContentType.AUDIO;
sampleRate: number;
bitDepth: StegaCassetteBitDepth;
channels: StegaCassetteChannels;
encoding: StegaCassetteEncoding;
borderWidth: number;
}
export interface StegaMetadataMusic extends Omit<StegaMetadataAudio, "type"> {
type: StegaContentType.MUSIC;
bpm: number;
semitones: number;
}
interface StegaMetadataString {
type: StegaContentType.STRING;
messageCount: number;
encoding: Stega64Encoding;
borderWidth: number;
}
interface StegaMetadataRobust {
type: StegaContentType.ROBUST;
redundancyLevel: number;
messageCount: number;
blockSize: 2 | 4;
encoding: "hex" | "base16";
}
interface StegaMetadataBinary {
type: StegaContentType.BINARY;
dataLength: number;
borderWidth: number;
sessionId: number; // 0 = keyless, non-zero = requires matching key
}
interface StegaMetadataKey {
type: StegaContentType.KEY;
borderWidth: number;
sessionId: number;
}
View StegaMetadata for an image.
Choose an image to see its metadata here
StegaBinary encodes arbitrary binary data into images.
StegaBinary.encode({ source: HTMLImageElement | HTMLCanvasElement;
data: Uint8Array; mimeType: string; borderWidth?: number;
aspectRatio?: number; key?: boolean | HTMLImageElement |
HTMLCanvasElement; sessionId?: number; }): EncodeResult
const fileBytes; // 0 bytes from "select a file"
const { key, encoded, sessionId } = StegaBinary.encode({
source,
data: fileBytes,
mimeType: "application/octet-stream",
borderWidth: 1,
aspectRatio: undefined,
key: // source image is also the key
});
StegaBinary.decode({ encoded: HTMLImageElement | HTMLCanvasElement;
key?: HTMLImageElement | HTMLCanvasElement; }): { mimeType: string;
data: Uint8Array; }
// Decode with key (for keyed or custom key)
const { mimeType, data } = StegaBinary.decode({ encoded, key });
// Decode keyless (sessionId = 0)
const { mimeType, data } = StegaBinary.decode({ encoded });
// Download decoded binary data as a file
downloadBytes({ data, mimeType, fileName: "decoded.bin" });
Decoded output will appear here
Animate steganographic image
new StegaAnimator({ resolution: number; source: HTMLImageElement |
HTMLCanvasElement; fadeAmount?: number; rotationMode?: "2d" | "3d";
shape?: "circle" | "square" | "implicit"; })
const animator = new StegaAnimator({
source,
resolution: ,
fadeAmount: ,
rotationMode: "",
shape: ""
});
document.body.appendChild(animator.canvas);
await animator.animate({
from: { rotation: Math.PI, scale: 0.0, x: 0.5, y: 0.5, },
to: { rotation: Math.PI * 4, scale: 0.5, x: 0.5, y: 0.5, },
rate: 0.005,
});
const killLoop = animator.animationLoop([
{
from: { rotation: 0, scale: 0.5, x: 0.5, y: 0.5, },
to: { rotation: Math.PI * 1, scale: 0.6, x: 0.5, y: 0.5, },
rate: ,
},
{
from: { rotation: Math.PI * 1, scale: 0.6, x: 0.5, y: 0.5, },
to: { rotation: Math.PI * 2, scale: 0.5, x: 0.5, y: 0.5, },
rate: ,
},
]);
killLoop();
Load an audio buffer from a url string
async loadAudioBuffersFromAudioUrl({ url: string; audioContext:
AudioContext; channels: StegaCassetteChannels; sampleRate?: number;
}): Promise<Float32Array[]>
const audioContext = new AudioContext();
const audioBuffer = await loadAudioBuffersFromAudioUrl({
url: "./example.mp3",
audioContext,
sampleRate: audioContext.sampleRate,
});
Load an image from a url string
async loadImageFromImageUrl({ url: string }):
Promise<HTMLImageElement>
const audioBuffer = await loadImageFromImageUrl({
url: "./example.jpg"
});
Play decoded audio buffers
async playDecodedAudioBuffers({ audioBuffers: Float32Array[];
audioContext: AudioContext; sampleRate?: number; }):
Promise<AudioBufferSourceNode>
const source = await playDecodedAudioBuffers({
audioBuffers,
audioContext,
sampleRate: audioContext.sampleRate,
});
source.stop();
Turn an HTML element into a file drop area
createDropReader({ element: HTMLElement; onSuccess: (element:
HTMLImageElement | HTMLAudioElement) => void; onFailure?: (message:
string) => void; onDragEnter?: () => void; onDragLeave?: () => void;
onDrop?: () => void; types?: (AudioType | ImageType)[]; }):
void
const element = document.body;
createDropReader({
element,
onSuccess: (image) => element.appendChild(image),
onFailure: (message) => console.error(message),
onDragEnter: () => element.classList.add("droppable"),
onDragLeave: () => element.classList.remove("droppable"),
onDrop: () => element.classList.remove("droppable"),
types: ["image/*"]
});
Turn an HTML input into a file input
createFileReader({ element: HTMLInputElement; onSuccess: (element:
HTMLImageElement | HTMLAudioElement) => void; onBinarySuccess?:
(result: { data: Uint8Array; mimeType: string; fileName: string })
=> void; onFailure?: (message: string) => void; types?: (AudioType |
ImageType | VideoType | "*/*")[]; }): void
const element = document.createElement("input");
createFileReader({
element,
onSuccess: (image) => document.body.appendChild(image),
onFailure: (message) => console.error(message),
types: ["image/*"]
});
// Or for binary data (any file type)
createFileReader({
element,
onBinarySuccess: ({ data, mimeType, fileName }) => {
console.log(`Loaded ${fileName} (${mimeType}): ${data.length} bytes`);
},
types: ["*/*"]
});
Read a file as binary bytes
readFileAsBytes({ file: File }): Promise<{ data: Uint8Array;
mimeType: string; fileName: string }>
const input = document.createElement("input");
input.type = "file";
input.onchange = async () => {
const file = input.files[0];
const { data, mimeType, fileName } = await readFileAsBytes({ file });
console.log(`Read ${fileName} (${mimeType}): ${data.length} bytes`);
};
Convert binary data to a Blob URL
bytesToBlobUrl({ data: Uint8Array; mimeType: string }):
string
const url = bytesToBlobUrl({ data: myBytes, mimeType: "image/png" });
const img = document.createElement("img");
img.src = url;
document.body.appendChild(img);
// Remember to revoke when done
URL.revokeObjectURL(url);
Download binary data as a file
downloadBytes({ data: Uint8Array; mimeType: string; fileName:
string }): void
downloadBytes({
data: myBytes,
mimeType: "application/pdf",
fileName: "document.pdf"
});