@majikah/majik-bytes
v1.0.0
Published
MajikBytes is a TypeScript utility for lossless byte conversion and portable data encoding. It preserves source types and encodes strings, JSON, binary data, Files, numbers, BigInts, and booleans as PNG, TXT, or JSON.
Maintainers
Readme
MajikBytes
MajikBytes is a TypeScript utility for lossless conversion between JavaScript values, raw bytes, and portable media representations.
It can wrap strings, JSON, binary data, files, numbers, BigInts, and booleans into a versioned byte representation that preserves the original source type. The payload can then be serialised as:
- PNG
- TXT
- JSON
MajikBytes is designed around deterministic binary processing, explicit metadata, and integrity verification.
- MajikBytes
- Architecture
- PNG Decoding
- Overview of the Data Flow
- Features
- Installation
- Quick Start
- API Reference
- PNG
- TXT
- Type Restoration
- Type Guards
- Validation
- Getters
- JSON Representation
- Error Handling
- Design Notes
- Use Cases
Overview
What is MajikBytes?
A MajikBytes instance is a structured representation of a value backed by a prefixed Uint8Array.
The prefix stores:
- the original source type;
- the payload format version;
- filename and MIME type metadata for
Filevalues.
The raw payload follows the prefix.
This allows MajikBytes to restore a value without requiring the caller to remember which type was originally supplied.
const value = await MajikBytes.create({
message: "Hello, Majikah!",
count: 42,
});
const restored = await value.restore();The restored value retains its original logical type.
Supported Source Types
MajikBytes currently supports:
| Source type | Restoration |
| ------------- | ------------------ |
| string | toString() |
| json | toJSONValue<T>() |
| uint8array | toUint8Array() |
| arraybuffer | toArrayBuffer() |
| blob | toBlob() |
| file | toFile() |
| number | toNumber() |
| bigint | toBigInt() |
| boolean | toBoolean() |
The original type can also be inspected with the corresponding is*() type guards.
Architecture
1. Versioned Type Prefix
Every MajikBytes payload begins with a compact prefix:
| Field | Byte/Description | | ------- | --------------------- | | Byte 0 | Source type tag | | Byte 1 | Prefix format version | | Payload | Raw data |
For File values, the prefix additionally contains the UTF-8 encoded filename and MIME type.
The current source-type tags are stable serialized values:
0x00→ string0x01→ json0x02→ uint8array0x03→ arraybuffer0x04→ blob0x05→ file0x06→ number0x07→ bigint0x08→ boolean
The prefix format is versioned so future structural changes can be introduced without silently changing the meaning of existing payloads.
2. Lossless PNG Encoding
MajikBytes stores its prefixed payload in standard RGBA PNG pixel data.
Three payload bytes are stored per pixel:
- R = byte 0
- G = byte 1
- B = byte 2
- A = 255
The alpha channel is always opaque.
This keeps the encoded pixel representation deterministic and prevents alpha premultiplication from becoming part of the payload representation.
PNG structure
The generated PNG contains:
┌──────────────────────────┐
│ PNG signature │
├──────────────────────────┤
│ IHDR │
├──────────────────────────┤
│ IDAT │
│ │
│ data rows │
│ ─────────────── │
│ packed payload bytes │
│ │
│ sentinel row │
│ │
│ hash row │
├──────────────────────────┤
│ IEND │
└──────────────────────────┘PNG scanlines are stored without a PNG filter (filter type 0) and compressed using raw DEFLATE before being wrapped in a zlib stream for the IDAT chunk.
3. Sentinel Row
PNG images are rectangular, so the payload may not completely fill the final data row.
MajikBytes appends a dedicated sentinel row after the payload rows.
The first pixel of the sentinel row stores the exact prefixed payload length as a 24-bit big-endian value:
R = length >> 16
G = length >> 8
B = length
A = 255All remaining pixels in the sentinel row are zero-valued in RGB.
This allows the decoder to recover the exact payload length without relying on trailing zero padding.
4. SHA-3-512 Integrity Hash
MajikBytes computes a complete prefixed payload SHA-3-512 digest.
The resulting 64-byte digest is stored in the final PNG row.
During decoding:
- the payload length is recovered from the sentinel row;
- the payload is extracted;
- SHA-3-512 is recomputed;
- the computed digest is compared with the stored digest using a constant-time comparison.
A mismatch causes decoding to fail.
The digest provides integrity verification and corruption detection. It does not provide authentication or prove who created the payload.
PNG Decoding
Direct Binary Decoding
MajikBytes previously relied on browser image APIs such as:
createImageBitmap()
OffscreenCanvas
CanvasRenderingContext2Dfor PNG decoding.
The PNG decoder now uses fast-png directly.
PNG Blob
│
▼
ArrayBuffer
│
▼
fast-png
│
▼
RGBA Uint8ArrayThis avoids the browser graphics pipeline entirely during MajikBytes PNG decoding.
Why this changed
Browser privacy and anti-fingerprinting protections can alter or restrict behavior around graphics APIs. In particular, Canvas/ImageBitmap-based decoding caused compatibility problems in privacy-focused environments such as Brave.
Direct binary PNG decoding avoids that dependency and gives MajikBytes a deterministic byte-oriented decoding path.
The decoder therefore no longer needs Canvas or ImageBitmap just to recover the embedded bytes.
Overview of the Data Flow
flowchart TD
A["JavaScript value"] --> B["MajikBytes.create()"]
B --> C["Type normalization"]
C --> D["Versioned prefix"]
D --> E["Raw prefixed bytes"]
E --> F["PNG"]
E --> G["TXT"]
E --> H["JSON"]
F --> I["PNG binary"]
G --> J["Base64 + hash"]
H --> K["Base64 + length"]
I --> L["fast-png"]
L --> M["RGBA bytes"]
M --> N["Sentinel + hash verification"]
N --> O["Prefix decoding"]
O --> P["Original value"]Features
- Lossless byte preservation — raw payload bytes are preserved without lossy image processing.
- Multiple source types — supports strings, JSON, binary data, files, numbers, BigInts, and booleans.
- Automatic type restoration — recover the original logical value without manually tracking its type.
- PNG serialisation — embeds payload bytes inside a standard RGBA PNG.
- Direct PNG decoding — uses
fast-pngrather than Canvas/ImageBitmap-based decoding. - Browser privacy friendly — avoids Canvas decoding paths that can trigger anti-fingerprinting behavior.
- SHA-3-512 integrity verification — detects corruption or modifications to the encoded payload.
- Versioned payload format — prefix format changes can be versioned explicitly.
- File metadata preservation — original filename and MIME type are retained for
Filevalues. - Portable TXT representation — Base64 payload with an integrity digest.
- JSON representation — Base64 payload with an explicit byte-length check.
- TypeScript-first API — strongly typed source identifiers, restoration methods, and validation results.
- Apache-2.0 licensed — suitable for personal and commercial use.
Installation
npm install @majikah/majik-bytesQuick Start
Create and encode a value
import { MajikBytes } from "@majikah/majik-bytes";
const originalData = {
text: "Hi there!",
id: 42,
};
const majikBytes = await MajikBytes.create(originalData);
const pngBlob = await majikBytes.toPNG();pngBlob is a standard image/png Blob and can be uploaded, stored, or downloaded normally.
Decode and restore
const decoded = await MajikBytes.fromPNG(pngBlob);
if (decoded.isJSON()) {
const data = decoded.toJSONValue<{
text: string;
id: number;
}>();
console.log(data.text);
// Hi there!
}Or restore without explicitly knowing the original type:
const value = await decoded.restore();
console.log(value);API Reference
Factory Methods
MajikBytes.create(input: unknown)
Creates a MajikBytes instance from a supported JavaScript value.
const bytes = await MajikBytes.create("Hello");Returns
Promise<MajikBytes>MajikBytes.fromJSON(json: unknown)
Restores a MajikBytes instance from its JSON representation.
const restored = MajikBytes.fromJSON({
data: "...",
length: 42,
});Returns
MajikBytesThrows MajikBytesError when the structure or declared payload length is invalid.
PNG
MajikBytes.toPNG()
Encodes the prefixed payload into a lossless PNG.
const png = await majikBytes.toPNG();Returns
Promise<Blob>The resulting Blob uses:
image/pngMajikBytes.fromPNG(blob: Blob)
Decodes a MajikBytes PNG directly from its binary representation.
const bytes = await MajikBytes.fromPNG(blob);The decoder:
- decodes the PNG using
fast-png; - locates the sentinel row;
- recovers the exact payload length;
- extracts the prefixed payload;
- verifies the SHA-3-512 digest;
- decodes the source-type prefix.
Returns
Promise<MajikBytes>Throws MajikBytesError when the PNG is malformed, the sentinel cannot be located, or the integrity digest does not match.
TXT
majikBytes.toTXT()
Serialises the prefixed payload as a plain-text representation containing:
Line 1 → MAJIKBYTES:v{version}
Line 2 → Base64 encoded prefixed payload
Line 3 → SHA-3-512 digest as hexadecimalconst txt = await majikBytes.toTXT();Returns
Promise<Blob>with:
text/plainMajikBytes.fromTXT(blob: Blob)
Restores a MajikBytes instance from a TXT representation created by toTXT().
const bytes = await MajikBytes.fromTXT(blob);The payload digest is verified before the instance is constructed.
Type Restoration
MajikBytes provides explicit restoration methods for each supported source type.
| Method | Return type | Source type |
| ------------------ | ------------------ | ---------------------- |
| toString() | string | string |
| toJSONValue<T>() | T | json |
| toUint8Array() | Uint8Array | uint8array |
| toArrayBuffer() | ArrayBuffer | arraybuffer |
| toBlob() | Blob | blob |
| toFile() | File | file |
| toNumber() | number | number |
| toBigInt() | bigint | bigint |
| toBoolean() | boolean | boolean |
| restore() | Promise<unknown> | Automatically selected |
Type-specific restoration methods verify the stored source type before returning a value.
For example:
const value = await MajikBytes.create("hello");
value.toString(); // ✅
value.toJSONValue(); // ❌ TYPE_MISMATCHType Guards
Each supported source type has a corresponding type guard:
isString()
isJSON()
isUint8Array()
isArrayBuffer()
isBlob()
isFile()
isNumber()
isBigInt()
isBoolean()Example:
const value = await MajikBytes.fromPNG(blob);
if (value.isFile()) {
const file = value.toFile();
console.log(file.name);
}These guards also provide TypeScript narrowing for sourceType.
Validation
MajikBytes provides validation helpers for the serialised PNG and TXT formats.
MajikBytes.isValidPNG(blob: Blob | File)
Validates a PNG as a MajikBytes payload.
const result = await MajikBytes.isValidPNG(file);
console.log(result.isValid);
console.log(result.message);Returns
Promise<MajikBytesValidationResult>The validator checks the MIME type, PNG structure, sentinel row, payload extraction, and SHA-3-512 integrity.
MajikBytes.isValidTXT(blob: Blob | File)
Validates a TXT representation by attempting to decode and verify it.
const result = await MajikBytes.isValidTXT(file);Returns
Promise<MajikBytesValidationResult>Getters
| Getter | Type | Description |
| -------------------- | ----------------------- | ------------------------------------------- |
| sourceType | MajikBytesSourceType | Original source type |
| version | number | Prefix format version |
| fileMeta | FileMeta \| undefined | Filename and MIME type for File sources |
| bytes | Uint8Array | Defensive copy of raw payload bytes |
| prefixedBytes | Uint8Array | Defensive copy of complete prefixed payload |
| byteLength | number | Raw payload byte length |
| prefixedByteLength | number | Complete prefixed payload byte length |
JSON Representation
MajikBytes can also be represented as a normal JSON object:
const json = majikBytes.toJSON();The structure is:
interface MajikBytesJSON {
data: string;
length: number;
}Where:
datais the Base64-encoded complete prefixed payload;lengthis the decoded byte length of that payload.
This allows MajikBytes values to travel through JSON-compatible systems without losing their binary representation.
Error Handling
MajikBytes uses a dedicated MajikBytesError class.
try {
const bytes = await MajikBytes.fromPNG(blob);
} catch (error) {
if (error instanceof MajikBytesError) {
console.error(error.code);
console.error(error.message);
}
}Available error codes:
| Code | Meaning |
| ------------------ | ---------------------------------------------------------------- |
| INVALID_INPUT | Input is missing, invalid, or unsupported |
| CORRUPT_DATA | Encoded data is malformed or fails integrity checks |
| TYPE_MISMATCH | Requested restoration type does not match the stored source type |
| NOT_IMPLEMENTED | Requested operation is intentionally unsupported |
| ASSERTION_FAILED | Internal validation assertion failed |
The error code is intended for programmatic handling and is more stable than parsing message.
Design Notes
No Canvas Dependency for PNG Decoding
PNG decoding is intentionally performed from raw PNG binary data.
MajikBytes does not require:
createImageBitmap()
OffscreenCanvas
CanvasRenderingContext2Dto decode its embedded payload.
This makes the decoding path independent of browser graphics rendering behavior and avoids compatibility problems caused by anti-fingerprinting mechanisms in privacy-focused browsers.
The PNG is treated as a binary container rather than as a rendered image.
Lossless Does Not Mean Authenticated
MajikBytes uses SHA-3-512 to detect unexpected changes to the payload.
This provides:
- corruption detection;
- accidental modification detection;
- integrity verification.
It does not provide:
- digital signatures;
- author authentication;
- proof of origin;
- non-repudiation.
Applications requiring authenticity should use a dedicated signature mechanism in addition to MajikBytes integrity verification.
File Metadata
For File inputs, MajikBytes preserves:
interface FileMeta {
filename: string;
mimetype: string;
}Example:
const bytes = await MajikBytes.create(file);
const restored = await bytes.toFile();
console.log(restored.name);
console.log(restored.type);Use Cases
MajikBytes can be useful anywhere binary data benefits from a portable, self-describing representation.
Binary data in image-compatible workflows
Embed arbitrary bytes inside a standard PNG container while retaining a structured payload format.
Portable data exchange
Move data through systems that naturally support images, text files, or JSON.
Tamper-evident payloads
Detect unexpected payload modification using SHA-3-512 integrity verification.
File preservation
Store binary files while retaining their original filename and MIME type.
Structured application state
Encode JSON or primitive values while retaining their original source type.
Contributing
If you want to contribute or help extend support to more platforms or file formats, reach out via email. All contributions are welcome!
License
Apache-2.0 — free for personal and commercial use.
Author
Developed by Josef Elijah Fabian (Zelijah) | Majikah Solutions OPC
Developer: Josef Elijah Fabian
GitHub: https://github.com/Majikah
Project Repository: https://github.com/Majikah/majik-signature
Contact
- Business Email: [email protected]
- Official Website: https://www.thezelijah.world
- Majikah Ecosystem: https://majikah.solutions
