ABOUT

What is an SWF?

Shock Wave Flash — a format from another era

More than a video file

An SWF — originally standing for ShockWave Flash, later rebranded as Small Web Format — is a binary media format developed by Macromedia and later run by Adobe. From the late 1990s through the early 2010s, it was one of the only ways to play interactive content on the web. This included games, animations, entire applications, and multimedia experiences all packed into SWF files.

Calling an SWF a "video file" undersells it considerably. An SWF is a complete compiled program; It contains a timeline, a virtual machine, bytecode, embedded assets, and interactive logic — all packed into a single binary SWF file, while a video file simply plays back a fixed stream of frames. A SWF can branch, respond to input, load external data, and execute code; The two are fundamentally different things.

WasFlash currently supports visual Video SWF playback only. Audio output and ActionScript execution are not yet implemented — so interactive SWFs and anything relying on pure animation won't behave as expected.

Inside the binary

A SWF file begins with a 3-byte signature — either FWS for uncompressed files, CWS for zlib-compressed (introduced in SWF 6), or ZWS for LZMA-compressed (SWF 13+). Everything after the 8-byte header is a sequence of tagged data blocks called records.

// SWF file header layout Bytes 0–2 : Signature ("FWS", "CWS", or "ZWS") Byte 3 : Version (u8, e.g. 0x0E = SWF 14) Bytes 4–7 : FileLength (u32 little-endian, uncompressed size) Bytes 8–... : RECT (frame size, variable-length bit-packed) FrameRate (u16 fixed-point 8.8) FrameCount (u16) [Tag records follow...]

The RECT structure alone illustrates how aggressively SWF packs data. Rather than aligning values to byte boundaries, it stores its four coordinates in variable-width bit fields: the first 5 bits encode the bit-length of each coordinate, and the coordinates follow immediately at that width. Parsing it requires a bit-level reader, not a standard byte stream.

Each tag record consists of a 16-bit field where the upper 10 bits are the tag type and the lower 6 bits are the length — unless the length is 63 or more, in which case a separate 32-bit length field follows. There are over 90 distinct tag types covering everything from shape definitions and bitmaps to font tables, sound streams, and bytecode.

DefineShape PlaceObject DefineBitsLossless DefineSprite DoAction DefineFont SoundStreamBlock ShowFrame

Shapes, not pixels

Flash content is vector-based. Shapes in a SWF are not stored as raster images — they are defined using fill styles, line styles, and edge records that describe curves and lines in twips (twentieths of a point, or 1/1440th of an inch). The renderer recomputes everything at display time, which is why Flash content scaled cleanly to any resolution long before high-DPI displays existed.

Curves use quadratic Bézier segments encoded as straight or curved edge records. Straight edges store delta-X and delta-Y offsets in variable-length bit fields. Curved edges store two control point deltas and an anchor delta, again in bit-packed form. Reconstructing a shape means walking these edge records and accumulating a path.

WasFlash uses WebAssembly compiled from a native SWF renderer (Ruffle-adjacent architecture) to handle this geometry pipeline in the browser — no plugins required, no Adobe runtime.

The bit-level reality

Most binary formats operate on byte-aligned fields. SWF does not. A significant portion of its structures are packed at the bit level, which means a correct parser must maintain a bit cursor independently of the byte cursor at all times. Reading a single RECT requires consuming an exact number of bits that isn't known until you read the first 5.

Coordinate values are stored as signed integers in two's complement at whatever bit-width the header specifies. A 15-bit signed value uses bit 14 as the sign bit, with the remaining 14 bits as the magnitude in standard two's complement — meaning the range is −16384 to +16383 at that width. The parser has to sign-extend these values correctly before they make sense in world space.

// Reading a bit-packed signed integer (conceptual) fn read_sbits(reader, n: usize) -> i32 { let raw = reader.read_ubits(n); // read n bits unsigned let sign_bit = 1 << (n - 1); if raw & sign_bit != 0 { (raw as i32) - (1 << n) // sign-extend } else { raw as i32 } }

On top of the geometry layer sits a full display list model: objects are placed, moved, and removed across frames using PlaceObject and RemoveObject tags. The renderer must maintain this list across every ShowFrame tag to produce correct output — it's stateful, not frame-independent like a video codec.

And underneath all of that, later SWF versions embed ActionScript bytecode — first AVM1 (a stack-based virtual machine for AS1/AS2), and later AVM2 (a register-based VM for AS3 with typed operands, exception handling, and a full class system). Executing that correctly is a separate and substantial problem, which is why full SWF support remains an ongoing engineering effort rather than a solved one.

What WasFlash is doing

WasFlash runs a WebAssembly build of a native SWF renderer directly in your browser. The WASM binary handles all parsing, display list management, and vector rasterization. The JavaScript layer sets the required cross-origin headers (COOP/COEP) to enable SharedArrayBuffer, which allows the renderer to use POSIX threads via Emscripten's pthread support — the same threading model it would have on a native system.

The result is a zero-install, zero-plugin SWF player that runs entirely client-side. The hope is that one day you will be able to just embed this into your website, and it will play whatever content you please(Usually games).

WebAssembly pthreads via Emscripten SharedArrayBuffer COOP / COEP headers Client-side only
← Back to player