What Is Actually Inside a SWF File
The format that made Flash portable was a tightly engineered binary container, and its structure explains both the plug-in's reach and its opacity

A SWF file is not a directory, not an archive in the conventional sense, not a document — it is a tagged binary stream, and everything Flash Player ever did on a web page came from reading that stream correctly. Understanding what the format actually contains helps explain why the format was so durable, why it was so difficult to reverse-engineer, and why archivists now need custom tooling just to see what they are preserving.

The envelope: header, compression, and version byte
Every SWF begins with a three-byte signature, either FWS for uncompressed, CWS for zlib-compressed (introduced in Flash Player 6), or ZWS for LZMA-compressed (introduced in Flash Player 13). That first byte alone tells a conforming reader everything it needs to know about how to decompress the rest of the payload. One byte follows to encode the SWF version — an integer from 1 to 45 across the format's commercial life — and then a four-byte little-endian integer records the total uncompressed file length. The next few bytes encode the stage dimensions as a packed rectangle using a variable-length bitfield structure Adobe called RECT, where even the number of bits per value is itself stored in the first five bits. It is the format's most immediate signal that compactness was the original design priority, not readability: this is a format designed for dial-up modems, not for parsing by hand.

After the RECT come two bytes encoding the frame rate as a fixed-point number — frames per second stored as integer.fraction — and two bytes for the total frame count. That is the complete header. Everything from that point onward is a sequence of tags.
Tags: the grammar of a SWF
A tag is a record type that pairs a type identifier with a body of arbitrary length. Short tags encode the type and length together in a single two-byte word: the upper ten bits identify the record type and the lower six bits give the body length in bytes, allowing bodies up to 62 bytes inline. Any body longer than 62 bytes uses a long-form tag, where those lower six bits are all set to 1 and a separate four-byte integer follows with the true length. This scheme means a player can skip any tag it does not recognise without corrupting the stream — an extension point baked into the grammar from the beginning, and one reason why Adobe could add features across decades without breaking older content.
Chronology
- Flash Player 6zlib (
CWS) compression added to the SWF envelope - Flash Player 9
DoABC/ AVM2 bytecode replacedDoAction/ AVM1 for ActionScript 3 - Flash Player 10AAC audio support added via
DefineSound - Flash Player 13LZMA (
ZWS) compression added - 25 July 2017Adobe's end-of-life announcement; format specification already public under open specification promise
The SWF file format specification, which Adobe released publicly under an open specification promise, catalogues well over one hundred distinct tag types. The most structurally important are DefineShape and its successors (DefineShape2 through DefineShape4), which encode vector geometry as fill styles, line styles and lists of drawing commands called SHAPERECORD arrays. Each drawing command is itself a bitfield: a move-to, a straight-line edge or a quadratic Bézier edge, all packed to the bit level to keep file sizes small. Flash chose quadratic rather than cubic Bézier curves — the same choice made by TrueType — for exactly that reason: one control point instead of two, smaller numbers, smaller files.
Also in The Format
FutureSplash Animator Shipped in November 1996 and Macromedia Bought It Six Weeks Later
ActionScript 1, 2 and 3 Are Three Different Languages That Happen to Share a Name
Bitmaps enter through DefineBitsLossless and DefineBitsJPEG tag families. Lossless bitmaps are zlib-compressed palettised or ARGB pixel arrays; JPEG tags carry a raw JPEG stream. Audio enters through DefineSound, which can wrap uncompressed PCM, ADPCM, MP3, Nellymoser or, from Flash Player 10, AAC. Every asset — shape, bitmap, font, sound, video frame — is assigned a character ID, a 16-bit integer unique within the file. That ID is the mechanism by which a PlaceObject tag reaches into the dictionary and puts a specific symbol on stage at a specific frame, depth and transform matrix.
Audio enters through
DefineSound, which can wrap uncompressed PCM, ADPCM, MP3, Nellymoser or, from Flash Player 10, AAC.
The timeline and display list
The timeline is not stored as a single data structure but reconstructed frame by frame from control tags. ShowFrame is the heartbeat of the format: every time a SWF decoder reads a ShowFrame tag, it renders the current display list and advances by one frame. Between ShowFrame tags sit PlaceObject, RemoveObject and SetBackgroundColor records that mutate the display list incrementally. A symbol placed at depth 3 in frame 1 remains at depth 3 in frame 2 unless a RemoveObject or an overriding PlaceObject says otherwise. The format is therefore differential — an early optimisation for streaming over slow connections that also became a source of subtle parsing edge cases, since correct playback requires accumulating state continuously rather than jumping to any frame independently.
Movie clips, the reusable animated containers that gave Flash its compositional power, are defined by DefineSpriteTag, which wraps its own inner sequence of control tags and ShowFrame markers. A sprite is effectively a nested SWF: its own timeline, its own display list, its own frame rate (well, its own frame count — sprite frame rate inherits from the root timeline). Nesting could go arbitrarily deep, and each level brought its own scope for ActionScript interaction.
Where this sits
FutureSplash to Adobe Animate: the software, the file format, and the virtual machine that ran inside a billion browsers.
Format anatomy
FWS / CWS / ZWS | the three-byte SWF signature encoding compression type (none, zlib, LZMA) |
RECT | variable-length bitfield encoding stage dimensions; bit-count is self-describing |
| Short tag / long tag | two-byte record where lower six bits encode body length; 63 triggers a four-byte overflow length |
ShowFrame | control tag that advances the display list by one frame; the format's clock tick |
| Character ID | 16-bit integer assigned to every asset in the SWF dictionary; used by PlaceObject to reference it |
Bytecode: from DoAction to ABC
ActionScript is stored as bytecode within the SWF stream itself. In early versions, DoAction tags carried sequences of AVM1 opcodes — a simple stack machine with instructions like Push, Add, GetVariable and CallFunction. These opcodes are human-readable enough that early decompilers could reconstruct something close to the original source. From Flash Player 9 onward, ActionScript 3 bytecode was stored in a completely different structure: DoABC tags containing AVM2 .abc files. An .abc file is its own layered format, with a constant pool for strings, integers and doubles, a method table, class definitions, and verified bytecode for each method body. AVM2 bytecode is typed, verified before execution, and substantially harder to decompile than its predecessor — a necessary trade-off for performance, since the Tamarin virtual machine that executed it applied JIT compilation.
Why the binary envelope made archiving hard
The self-describing tag structure means a reader can walk a SWF without a schema, but it cannot interpret what it finds without knowing each tag type. Proprietary or undocumented tags — and several existed — are opaque blobs. Encrypted SWFs, though never part of the official specification, appeared in the wild and defeated standard extraction tools. Video is another layer: DefineVideoStream and VideoFrame tags wrap sequences in either Screen Video, Sorenson Spark or On2 VP6 codec formats, the last two being proprietary codecs whose inclusion required separate licensing agreements that had nothing to do with Adobe.
These layers of compression, codec licensing and version-specific behaviour are exactly what makes the Ruffle re-implementation project technically demanding and why the Internet Archive's emulated Flash collection continues to mark some titles as partially or non-functional. What looks like a single file is in practice a versioned, layered, differentially-encoded system — one that Flash Player spent twenty-five years learning to execute, and that open-source tooling is still working to fully reconstruct.