⚡ Graphics & WebGL

Flash Vector Graphics to SVG & HTML5 Canvas: Automated AST Parsing & Bezier Math

👤 Author: Lead ActionScript & WebAssembly Architect📅 Technical Review: September 2026⚡ WebAssembly & WebGL 2.0 Verified

The Adobe Flash SWF specification remains one of the most mathematically compact vector graphics file formats ever devised. Created in an era of dial-up modems, Flash encoded intricate vector illustrations, typography, and interactive button states into binary bitstreams using integer twips and quadratic Bezier curves. Modernizing these vector assets into scalable SVG paths and HTML5 Canvas 2D / WebGL contexts requires automated binary AST parsing and precise geometric coordinate transformations.

1. The SWF Binary Vector Format: Twips, Bit Packing & DefineShape Tags

Inside a compiled .swf binary, vector geometry is encapsulated within DefineShape tags (specifically DefineShape [Tag ID 2], DefineShape2 [ID 22], DefineShape3 [ID 32 with 8-bit alpha], and DefineShape4 [ID 83 with non-scaling strokes]).

To achieve extreme byte compression, SWF eliminates floating-point representations entirely:

  • Twip Coordinates: All coordinates (x, y, widths, bounding boxes) are recorded in twips (twentieth of an imperial point). Exactly 20 twips equal 1 logical display pixel. A 100x100 pixel graphic is stored internally as a 2000x2000 twip bounding box.
  • Variable Bit-Field Packing: Shape coordinates are packed using arbitrary bit-width fields (e.g., a 13-bit signed integer) rather than byte-aligned structures. A binary parser must read variable bit streams (readSB(numBits)) to unpack vertex offsets.
  • Left & Right Fill Indexing: Unlike SVG paths where each path element owns its fill color, SWF edge records carry dual fill indices: FillStyle0 (left-hand fill) and FillStyle1 (right-hand fill). This allowed Flash to define intersecting geometric meshes without storing redundant edge segments.

2. Mathematical Curve Elevation: Quadratic to Cubic Bezier Algorithms

In the SWF specification, all curved lines are encoded as quadratic Bezier curves (CurvedEdgeRecords). A quadratic curve is evaluated parametrically across ( t in [0, 1] ) using a start coordinate ( P_0 ), an anchor/end coordinate ( P_1 ), and a single control point ( QP ):

B_quad(t) = (1 - t)^2 * P0 + 2 * (1 - t) * t * QP + t^2 * P1

While SVG includes the quadratic Q and T commands, modern vector engines, font glyph exporters, and GPU tessellators standardize on cubic Bezier curves (C command in SVG path data, ctx.bezierCurveTo() in HTML5 Canvas). A cubic Bezier curve features two control points (( CP_1 ) and ( CP_2 )):

B_cubic(t) = (1 - t)^3 * P0 + 3 * (1 - t)^2 * t * CP1 + 3 * (1 - t) * t^2 * CP2 + t^3 * P1

To losslessly elevate a quadratic curve into a cubic curve without altering its spatial curvature or geometric tangent vectors, we solve for ( CP_1 ) and ( CP_2 ) using degree elevation:

// Algorithmic Elevation of Quadratic Bezier to Cubic Bezier
interface Point2D { x: number; y: number; }

function elevateQuadraticToCubic(p0: Point2D, qp: Point2D, p1: Point2D) {
  // CP1 = P0 + (2/3) * (QP - P0)
  const cp1: Point2D = {
    x: p0.x + (2.0 / 3.0) * (qp.x - p0.x),
    y: p0.y + (2.0 / 3.0) * (qp.y - p0.y)
  };

  // CP2 = P1 + (2/3) * (QP - P1)
  const cp2: Point2D = {
    x: p1.x + (2.0 / 3.0) * (qp.x - p1.x),
    y: p1.y + (2.0 / 3.0) * (qp.y - p1.y)
  };

  return { cp1, cp2, p3: p1 };
}

This mathematical transformation guarantees that the generated SVG path precisely matches the anti-aliased silhouette rendered by the original Flash vector rasterizer.

3. Gradient Fill Matrices & Affine Transformation Translation

Flash gradients (both linear and radial) are not positioned using simple start/end points. Instead, Flash defines all gradients inside a canonical bounding square spanning from -16,384 to +16,384 twips (-819.2 to +819.2 pixels) along both X and Y axes.

The SWF GRADIENT record applies a 2x3 affine transform matrix:

// SWF Affine Gradient Matrix
| a   c   tx |
| b   d   ty |
| 0   0   1  |

When converting to an SVG <linearGradient> element, developers can map this transformation matrix directly to the gradientTransform attribute after converting translation coordinates from twips to pixels:

// Generating SVG Gradient Element with Native Transform Matrix
const a = matrix.scaleX;
const b = matrix.rotateSkew0;
const c = matrix.rotateSkew1;
const d = matrix.scaleY;
const tx = matrix.translateX / 20; // Convert twips to pixels
const ty = matrix.translateY / 20;

const svgGradient = 
  '<linearGradient id="grad_01" gradientUnits="userSpaceOnUse"
' +
  '  gradientTransform="matrix(1 0 0 1 120 80)"
' +
  '  x1="-819.2" y1="0" x2="819.2" y2="0">
' +
  '  <stop offset="0.0" stop-color="#38bdf8" stop-opacity="1.0"/>
' +
  '  <stop offset="1.0" stop-color="#1e293b" stop-opacity="1.0"/>
' +
  '</linearGradient>';

4. Comparison: Flash SWF vs. SVG vs. HTML5 Canvas

Specification Metric Flash SWF (DefineShape) Scalable Vector Graphics (SVG) HTML5 Canvas 2D API
Data Representation Binary packed bitstream tags. Human-readable XML DOM strings. Immediate-mode JavaScript imperative commands.
Unit Scale Integer Twips (1/20th pixel). Floating-point CSS Pixels (user units). Floating-point Device / Canvas Pixels.
Curve Primitives Quadratic Beziers exclusively. Cubic Beziers, Quadratic, Arcs. bezierCurveTo, quadraticCurveTo.
Fill Winding Rules Left/Right Edge Dual-Fill index. fill-rule="evenodd" or "nonzero". ctx.fill('evenodd') / ctx.fill('nonzero').

5. Automated AST Extraction & Modern Pipeline Tooling

Manual vector redrawing is commercially unviable for enterprise catalogs containing tens of thousands of animated assets. Automated extraction pipelines operate by parsing compiled SWF binaries into an abstract syntax tree (AST):

  • JPEXS Free Flash Decompiler (FFDec): An open-source Java-based SWF decompiler providing command-line automation for bulk asset extraction. It can export all shape tags directly to formatted SVG documents with gradient styling intact.
  • SWF2SVG / Animate CC Canvas Exporter: Compiles vector definitions into JavaScript modules containing optimized path instructions for CreateJS or PixiJS.
  • GPU Vector Tessellation: For real-time 3D and WebGL applications, extracted vector curves are triangulated via Ear-Clipping or Constrained Delaunay Triangulation (CDT) into vertex buffers, enabling 60 FPS GPU rasterization with custom shaders.

Frequently Asked Questions

What unit of measurement does Flash SWF use to store vector coordinate geometry?

Flash SWF binaries store all vector coordinates, line thicknesses, and bounding rectangles in "twips". One twip is defined as one-twentieth of a logical pixel (1 pixel = 20 twips). This integer-based fractional unit allowed early Flash players to execute sub-pixel vector geometry math using fast 32-bit integer arithmetic without the performance overhead of floating-point units on older CPU hardware.

Why do Flash vector curves require mathematical elevation to cubic Beziers in SVG and Canvas?

Flash SWF DefineShape tags represent all curved vector paths exclusively using quadratic Bezier curves (a start point, a single quadratic control point QP, and an end point). While HTML5 Canvas has quadraticCurveTo() and SVG has the Q command, modern rendering pipelines, SVG typography, and graphics tools operate primarily on cubic Bezier curves defined by two control points (CP1 and CP2). Converting quadratic curves to cubic curves ensures losslessly uniform path representations across all modern rendering engines.

What is the exact mathematical algorithm to convert a quadratic Bezier curve to a cubic Bezier?

Given a quadratic curve starting at P0, ending at P1, with control point QP, the two equivalent cubic control points CP1 and CP2 are derived as: CP1 = P0 + (2/3) * (QP - P0), and CP2 = P1 + (2/3) * (QP - P1). This degree-elevation algorithm guarantees that the evaluated position, tangent angle, and curvature across the parametric interval t in [0, 1] remain mathematically identical to the original quadratic curve.

How do Flash SWF DefineShape tags represent fills and outlines?

SWF shapes are stored in DefineShape tags (types 1 through 4) using a stream of SHAPERECORD structures. Fills are defined in a FILLSTYLEARRAY containing solid colors, linear gradients, radial gradients, or bitmap textures. Outlines are stored in a LINESTYLEARRAY. In SWF paths, edges carry fill style indexes for both their left and right sides (FillStyle0 and FillStyle1), enabling complex vector meshes to share boundary lines without duplicating vertex data.

How are Flash gradient transform matrices converted to SVG linearGradient and radialGradient coordinates?

In SWF binaries, gradient coordinates are defined in an abstract square measuring 32,768 x 32,768 twips centered at (0, 0). The SWF specifies a 2x3 affine transformation matrix (scale, skew, rotate, translate) that maps this canonical coordinate space onto the target geometry. Porting to SVG requires inverting this transform or calculating the gradient coordinate line vector (x1, y1) -> (x2, y2) by multiplying the transformed unit vectors.

Automate Your Flash Vector Extraction Pipeline

Do not lose high-fidelity vector artwork to obsolete Flash binaries. CodeForFlash builds customized extraction scripts, AST parsers, and automated SVG conversion pipelines for enterprise content owners.

Request a Vector Extraction Consultation →
CodeForFlash Technical Council

Written by CodeForFlash Technical Council

Senior interactive systems architect and digital preservation engineer specializing in ActionScript 3.0 virtual machines (AVM2), Rust/WebAssembly emulation engines (Ruffle), Stage3D to WebGL 2.0 shader compilation, and HTML5 vector rasterization.