113
LVL 04 — SENIOR-IN-TRAININGSESSION 113DAY 113

PROJECT FILE FORMAT

🎫 PIXELCRAFT-099
Feature | 🔴 Expert | Priority: 🟠 High

Users export final images but lose all their work — filters, selections, history. Design a custom .pxc project file format. Save everything: image data, filter settings, metadata. Load it back exactly as it was. Version the format for future compatibility.
CONCEPTS.UNLOCKED
📦
Custom File Format
Design a format that represents your application's state. .pxc = PixelCraft project. Like .psd (Photoshop), .fig (Figma), .sketch. Your format, your rules, your data model.
📋
JSON + Binary Packaging
Metadata in JSON, image data in binary blobs. JSON is human-readable and easy to parse. Binary (ArrayBuffer) is efficient for pixel data. Package both into one file using a structured container.
🔢
File Versioning
Version 1 files must open in version 5 of the app. Include a format version number. Each version's parser knows how to migrate from the previous. Forward compatibility is the hardest part of file format design.
🔄
Backward Compatibility
New app reads old files. Old app warns about new files. Migration functions upgrade v1→v2→v3. Unknown fields are preserved, not deleted. Graceful degradation for features the old app doesn't support.
🗜️
Compression
gzip reduces file size 60-80%. Raw ImageData for a 4K image = 33MB. Compressed = ~5MB. Use CompressionStream API (native browser) or pako library. Transparent to the user.
HANDS-ON.TASKS
01
Define the File Structure
// .pxc file structure: // [4 bytes] magic: "PXC\0" // [4 bytes] format version (uint32) // [4 bytes] metadata length (uint32) // [N bytes] metadata (JSON, gzipped) // [remaining] image data (gzipped) interface PXCMetadata { version: number; // format version appVersion: string; createdAt: string; updatedAt: string; canvas: { width: number; height: number; }; filters: { brightness: number; contrast: number; saturation: number; blur: number; }; history: { label: string; timestamp: number; }[]; customData: Record<string, unknown>; // ↑ extensible for future features }
02
Save: Serialize to .pxc
async function saveProject( metadata: PXCMetadata, imageData: ImageData ): Promise<Blob> { // 1. Compress metadata const metaJson = JSON.stringify( metadata); const metaBytes = await compress( new TextEncoder() .encode(metaJson)); // 2. Compress image data const imageBytes = await compress( imageData.data.buffer); // 3. Build file const header = new ArrayBuffer(12); const view = new DataView(header); // Magic bytes: "PXC\0" view.setUint8(0, 0x50); // P view.setUint8(1, 0x58); // X view.setUint8(2, 0x43); // C view.setUint8(3, 0x00); // \0 // Version view.setUint32(4, 1); // v1 // Metadata length view.setUint32(8, metaBytes.byteLength); return new Blob([ header, metaBytes, imageBytes ], { type: 'application/x-pixelcraft' }); } async function compress( data: ArrayBuffer ): Promise<ArrayBuffer> { const stream = new Blob([data]) .stream() .pipeThrough( new CompressionStream('gzip')); return new Response(stream) .arrayBuffer(); }
03
Load: Parse .pxc File
async function loadProject( file: File ): Promise<{ metadata: PXCMetadata; imageData: ImageData; }> { const buffer = await file.arrayBuffer(); const view = new DataView(buffer); // 1. Verify magic bytes const magic = String.fromCharCode( view.getUint8(0), view.getUint8(1), view.getUint8(2)); if (magic !== 'PXC') throw new Error( 'Not a PixelCraft file'); // 2. Read version const version = view.getUint32(4); // 3. Read metadata const metaLen = view.getUint32(8); const metaBytes = buffer.slice( 12, 12 + metaLen); const metaJson = new TextDecoder().decode( await decompress(metaBytes)); let metadata: PXCMetadata = JSON.parse(metaJson); // 4. Migrate if needed metadata = migrateMetadata( metadata, version); // 5. Read image data const imgBytes = buffer.slice( 12 + metaLen); const pixels = new Uint8ClampedArray( await decompress(imgBytes)); const imageData = new ImageData( pixels, metadata.canvas.width, metadata.canvas.height); return { metadata, imageData }; }
04
Version Migration
const CURRENT_VERSION = 1; function migrateMetadata( meta: any, fromVersion: number ): PXCMetadata { let data = { ...meta }; // v1 → v2 (future example): // if (fromVersion < 2) { // data.layers = [{ // id: 'base', // name: 'Background', // visible: true, // }]; // data.version = 2; // } // v2 → v3 (future example): // if (fromVersion < 3) { // data.colorProfile = 'srgb'; // data.version = 3; // } // Each migration is a pure function: // old shape → new shape. // Chain them: v1 → v2 → v3. // Old files always open correctly. if (fromVersion > CURRENT_VERSION) { console.warn( `File is v${fromVersion},` + ` app supports v${ CURRENT_VERSION}.` + ` Some features may not load.` ); } return data as PXCMetadata; }
05
Download & Open UI
// Save button handler async function handleSave() { const blob = await saveProject( getMetadata(), getCurrentImageData() ); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `project-${ Date.now()}.pxc`; a.click(); URL.revokeObjectURL(url); } // Open: file input with accept <input type="file" accept=".pxc" onChange={async (e) => { const file = e.target.files?.[0]; if (!file) return; const { metadata, imageData } = await loadProject(file); restoreState(metadata, imageData); }} />
06
Close the Ticket
git switch -c feature/PIXELCRAFT-099-pxc-format git add src/lib/fileFormat.ts git commit -m "Add .pxc project file format (PIXELCRAFT-099)" git push origin feature/PIXELCRAFT-099-pxc-format # PR → Review → Merge → Close ticket ✅
CS.DEEP-DIVE

File format design is protocol design.

The same principles apply to file formats, network protocols, and APIs: magic numbers, versioning, backward compatibility, and extensibility.

// Magic bytes identify file types:

PNG   → 89 50 4E 47 (.PNG)
PDF   → 25 50 44 46 (%PDF)
ZIP   → 50 4B 03 04 (PK..)
JPEG  → FF D8 FF
PXC   → 50 58 43 00 (PXC.)

// Versioning strategies:
Additive: new fields ignored by old
  (JSON APIs, protobuf)
Migration: transform v1→v2→v3
  (database schemas, file formats)
Dual: write latest, read all
  (most robust, most complex)

// Real formats are just this:
// .docx = ZIP of XML files
// .psd = header + layers + image
// .sqlite = header + B-tree pages
// Structure + data + versioning.
"File Format Lab"
[A]Add auto-save: every 30 seconds, save the current project state to IndexedDB. On next visit, detect the auto-save and offer to restore. "PixelCraft found an unsaved project. Restore?"
[B]Register the .pxc MIME type: add to the web app manifest so double-clicking a .pxc file opens PixelCraft (if installed as a PWA). Use the File Handling API (Chrome 102+).
[C]Research: how does the Figma .fig file format work? How does Photoshop's .psd work (there's a famous spec comment: "At this point, I'd like to take a moment to speak to you about the PSD format. PSD is not a good format.") Why is file format design so hard?
REF.MATERIAL
ARTICLE
MDN Web Docs
Native browser compression: gzip and deflate streams. No libraries needed. Compress and decompress data with the Streams API.
COMPRESSIONOFFICIALESSENTIAL
ARTICLE
MDN Web Docs
Read and write binary data: getUint32, setFloat64, byte offsets. The low-level API for parsing and building binary file formats.
BINARYOFFICIAL
VIDEO
Computerphile
Magic numbers, headers, data sections: how file formats identify and structure themselves. From PNG to ZIP to executable formats.
FILE FORMATTHEORY
ARTICLE
MDN Web Docs
Modern file access: showSaveFilePicker, showOpenFilePicker. Save directly to the user's filesystem without download dialogs. Chrome 86+.
FILE APIMODERN
ARTICLE
Wikipedia
Comprehensive list of magic bytes for file formats: PNG, JPEG, PDF, ZIP, ELF, and hundreds more. How software identifies file types.
MAGIC BYTESREFERENCE
// LEAVE EXCITED BECAUSE
Save → project.pxc (compressed, versioned). Open → everything restored exactly. Filters, settings, metadata — all preserved. You designed a real file format with magic bytes, versioning, and migration. This is how Photoshop, Figma, and every pro app works.