CSS visual effects (border-radius, box-shadow, filter: blur(), etc.) cannot be expressed in FCPXML in a way that Premiere Pro or DaVinci Resolve will honour. These effects exist on layers inside the Hyperframes composition. The solution is to render affected layers as ProRes 4444 video stems (with alpha channel) so the visual effect is baked into the pixels, but the layer remains editable as a clip on the timeline.
1. Python Render Pipeline — Selected Architecture
1.1 Decision Matrix
Renderer
border-radius
box-shadow
blur()
Alpha
Speed
Maintainability
Verdict
ffmpeg lavfi
❌ Manual math
❌ Very hard
⚠️ Frame-level only
⚠️ Clunky
✅ Fast
❌ Fragile
Reject
Pillow + ffmpeg
✅ Native rounded_rectangle
✅ Easy (blur + composite)
✅ GaussianBlur
✅ Full
⚠️ Slow CPU
✅ Excellent
Good for simple cases
Cairo + ffmpeg
✅ Native arcs
⚠️ Manual
❌ No native blur
✅ Full
⚠️ Medium
⚠️ Complex
Overkill
Skia-Python + ffmpeg
✅ RRect
✅ DropShadow filter
✅ Blur filter
✅ Full
✅ Fast (GPU/MT CPU)
✅ Good
Selected
1.2 Why Skia-Python Wins
Native CSS-like filters: skia.ImageFilters.DropShadow() and skia.ImageFilters.Blur() map directly to box-shadow and filter: blur().
Hardware-like compositing: Porter-Duff blend modes, antialiased vector paths, subpixel positioning — all the primitives needed for pixel-accurate CSS reproduction.
Performance: GPU backend (OpenGL/Vulkan) or multi-threaded CPU rasterizer. Orders of magnitude faster than Pillow for high-res/frame-count stems.
Industry-proven: Chrome, Flutter, and Android use Skia. It is the reference implementation for modern 2D graphics.
1ffmpeg -y \2 -f rawvideo -vcodec rawvideo -s 1920x1080 -pix_fmt rgba -r 30 -i - \3 -c:v prores_ks \4 -profile:v 4444\# MUST be 4444 — 444 has no alpha5 -pix_fmt yuva444p10le \# 10-bit YUV + alpha — ProRes 4444 standard6 -alpha_bits 16\# Full alpha bit depth for smooth gradients7 -qscale:v 9\# Quality (0–31, lower=better; 9–13 is production sweet spot)8 -vendor ap10 \# Apple ProRes vendor tag for NLE compatibility9 -movflags +write_colr \10 -colorspace bt709 \11 -color_primaries bt709 \12 -color_trc bt709 \13 output.mov
Encoder choice: prores_ks (Kostya Shishkov) is the cross-platform standard. prores_aw (Apple's wrapper) is macOS-only and less predictable on Windows/Linux. Do not useprores (old, alpha bugs).
2.2 Alpha Premultiplication — CRITICAL
Both Premiere Pro and Resolve expect premultiplied alpha for ProRes 4444. ffmpeg prores_ks with yuva444p10le outputs premultiplied by default.
Skia: kRGBA_8888 surfaces are premultiplied by default in most backends. ✅ No extra step needed.
Pillow: Image.RGBA is unpremultiplied. If using Pillow as fallback, premultiply before piping to ffmpeg:
The stem must match the sequence frame rate and the layer's in-point/out-point exactly. If the layer starts at 00:00:01:15 in a 30fps timeline and lasts 120 frames, the stem must be:
120 frames long
30 fps
Imported with start="0s" and offset="1.5s" (or 45/30s) in FCPXML.
Premiere Pro: Imports as Video 1, Video 2, Video 3, Video 4 (top-to-bottom in spine = bottom-to-top in timeline, depending on importer version; test with a 2-layer reference).
DaVinci Resolve: Imports as Track 1, Track 2, Track 3, Track 4. Alpha composites correctly on all upper tracks.
4. NLE Alpha Compositing Behaviour — Confirmed
4.1 Adobe Premiere Pro (tested behaviour, FCPXML import)
Question
Answer
Does Premiere read ProRes 4444 alpha from FCPXML?
✅ Yes. Premiere detects the alpha channel and enables it automatically.
layer_index: Zero-padded to match the source composition (e.g., 03, 07, 12).
layer_name: Human-readable name from the Hyperframes layer panel.
effect_tags: Comma-separated CSS effects that were baked (e.g., border_radius, box_shadow, blur, drop_shadow).
BAKED: Mandatory suffix. Flags the file as a rendered stem so editors know it is not raw media and should not be re-colour-graded or re-composited for the baked effect.
5.2 Sidecar Warning File (.stemwarn)
For every baked stem, write a sidecar .stemwarn file in the same directory:
json
1{2"stem":"layer_03_hero_card_border_radius_BAKED.mov",3"source_layer":"Hero Card",4"layer_index":3,5"baked_effects":["border-radius: 16px","box-shadow: 0 4px 12px rgba(0,0,0,0.15)"],6"original_css":{7"border-radius":"16px",8"box-shadow":"0 4px 12px rgba(0,0,0,0.15)",9"background":"#FF3366"10},11"render_settings":{12"codec":"ProRes 4444",13"resolution":"1920x1080",14"fps":30,15"duration_frames":120,16"premultiplied_alpha":true17},18"warning":"This file is a rendered stem. The CSS visual effects listed above are baked into the pixels. Do not apply additional blur, shadow, or corner-pin effects expecting to edit the original geometry. To change the effect, re-render from Hyperframes.",19"rendered_at":"2026-04-26T12:00:00Z",20"render_engine":"skia-python 1.0.0 + ffmpeg 7.0"21}
Purpose:
Editor sees .stemwarn in the bin/folder and knows the clip is baked.
QA/automation can parse JSON to verify that stems match the source composition.
Round-trip: re-importing the project into Hyperframes can read .stemwarn to reconstruct the original CSS parameters.
6. Complete Python Reference Implementation
See stem_renderer.py in this repository for a working Skia-Python + ffmpeg pipeline that:
Accepts a layer definition (position, size, colour, CSS effects).
Renders each frame to a Skia RGBA surface.
Pipes raw pixels to ffmpeg for ProRes 4444 encoding.