| name | blender-vse |
| description | Drive Blender's Video Sequence Editor to create and edit video timelines, add strips (text, color, movie, image, sound, effects), transitions, keyframe animations, and render video output. Use when the user mentions video editing, VSE, sequences, timelines, subtitles, text overlays, or video rendering in Blender. |
Blender VSE — Video Sequence Editor Skill
Python API reference for Blender 5.1+ Video Sequence Editor. Send all code via:
curl -s localhost:5656 --data-binary @- <<'PYEOF'
<python code>
PYEOF
See the main blender skill for full communication details, visual feedback, and error recovery.
Setup
Before using VSE, ensure the sequence editor exists:
scene = bpy.context.scene
if not scene.sequence_editor:
scene.sequence_editor_create()
se = scene.sequence_editor
Configure timeline:
scene.frame_start = 1
scene.frame_end = 250
scene.render.fps = 24
scene.render.resolution_x = 1920
scene.render.resolution_y = 1080
Creating strips
All strip creation is via scene.sequence_editor.strips.*:
Text strip
strip = se.strips.new_effect(name="Title", type="TEXT", channel=1, frame_start=1, length=120)
strip.text = "Hello World"
strip.font_size = 80
strip.location = (0.5, 0.5)
strip.color = (1, 1, 1, 1)
strip.use_shadow = True
strip.shadow_color = (0, 0, 0, 1)
strip.use_bold = True
strip.alignment_x = 'CENTER'
strip.wrap_width = 0.8
Color strip (solid background)
strip = se.strips.new_effect(name="BG", type="COLOR", channel=1, frame_start=1, length=120)
strip.color = (0.1, 0.1, 0.1)
Movie strip (video file)
strip = se.strips.new_movie(name="Clip", filepath="/path/to/video.mp4", channel=1, frame_start=1)
Image strip
strip = se.strips.new_image(name="Photo", filepath="/path/to/image.png", channel=1, frame_start=1)
strip.right_handle = strip.left_handle + 48
Sound strip
strip = se.strips.new_sound(name="Audio", filepath="/path/to/audio.mp3", channel=2, frame_start=1)
strip.volume = 0.8
Scene strip (render 3D scene into VSE)
strip = se.strips.new_scene(name="3D", scene=bpy.data.scenes["Scene"], channel=1, frame_start=1)
Adjustment layer
strip = se.strips.new_effect(name="Adjust", type="ADJUSTMENT", channel=5, frame_start=1, length=120)
Effect strips (require input strips)
1-input effects (SPEED, GLOW, GAUSSIAN_BLUR)
blur = se.strips.new_effect(name="Blur", type="GAUSSIAN_BLUR", channel=3, frame_start=1, length=60, input1=some_strip)
blur.size_x = 10
blur.size_y = 10
2-input effects (ALPHA_OVER, CROSS, WIPE, ADD, SUBTRACT, MULTIPLY, GAMMA_CROSS, ALPHA_UNDER, COLORMIX)
transition = se.strips.new_effect(name="Fade", type="CROSS", channel=3, frame_start=50, length=20, input1=strip_a, input2=strip_b)
Strip properties (common to all visual strips)
Transform (position, scale, rotation)
Every visual strip has a .transform property:
strip.transform.offset_x = 100
strip.transform.offset_y = -50
strip.transform.scale_x = 1.5
strip.transform.scale_y = 1.5
strip.transform.rotation = 0.785
Note: TRANSFORM is NOT a valid effect type. Use strip.transform directly.
Crop
strip.crop.min_x = 100
strip.crop.max_x = 100
strip.crop.min_y = 50
strip.crop.max_y = 50
Blending
strip.blend_alpha = 1.0
strip.blend_type = 'ALPHA_OVER'
strip.mute = False
strip.lock = False
Timing
strip.content_start
strip.left_handle
strip.right_handle
strip.duration
strip.left_handle_offset
strip.right_handle_offset
strip.content_duration
Setting strip duration: duration is read-only. Set it via right_handle:
strip.right_handle = strip.left_handle + desired_duration
Text strip — full property reference
strip.text = "Your text here"
strip.font_size = 60.0
strip.font = None
strip.use_bold = False
strip.use_italic = False
strip.wrap_width = 1.0
strip.location = (0.5, 0.5)
strip.alignment_x = 'CENTER'
strip.anchor_x = 'CENTER'
strip.anchor_y = 'CENTER'
strip.color = (1, 1, 1, 1)
strip.use_shadow = True
strip.shadow_color = (0, 0, 0, 1)
strip.shadow_offset = 0.04
strip.shadow_angle = 1.134
strip.shadow_blur = 0.0
strip.use_outline = True
strip.outline_color = (0, 0, 0, 1)
strip.outline_width = 0.05
strip.use_box = True
strip.box_color = (0, 0, 0, 0.5)
strip.box_margin = 0.01
strip.box_roundness = 0.0
Keyframing strip properties
Animate any strip property over time:
strip.blend_alpha = 0.0
strip.keyframe_insert(data_path="blend_alpha", frame=1)
strip.blend_alpha = 1.0
strip.keyframe_insert(data_path="blend_alpha", frame=24)
strip.location = (0.5, 0.0)
strip.keyframe_insert(data_path="location", frame=1)
strip.location = (0.5, 0.5)
strip.keyframe_insert(data_path="location", frame=30)
strip.transform.scale_x = 1.0
strip.transform.keyframe_insert(data_path="scale_x", frame=1)
strip.transform.scale_x = 2.0
strip.transform.keyframe_insert(data_path="scale_x", frame=60)
Managing strips
[(s.name, s.type, s.channel) for s in se.strips]
strip = se.strips["Title"]
se.strips.remove(strip)
se.channels[1].name = "Video"
se.channels[2].name = "Audio"
se.channels[1].mute = False
se.channels[1].lock = False
Rendering
Render single frame (PNG)
scene.render.filepath = f"{OUTPUT}/frame.png"
scene.render.image_settings.media_type = 'IMAGE'
scene.render.image_settings.file_format = 'PNG'
scene.render.resolution_percentage = 100
scene.frame_set(30)
bpy.ops.render.render(write_still=True)
Render animation (video)
scene.render.filepath = f"{OUTPUT}/render.mp4"
scene.render.image_settings.media_type = 'VIDEO'
scene.render.image_settings.file_format = 'FFMPEG'
scene.render.ffmpeg.format = 'MPEG4'
scene.render.ffmpeg.codec = 'H264'
scene.render.ffmpeg.constant_rate_factor = 'MEDIUM'
scene.render.ffmpeg.ffmpeg_preset = 'GOOD'
scene.render.ffmpeg.audio_codec = 'AAC'
bpy.ops.render.render(animation=True)
Use VSE output (not 3D viewport)
To render VSE output instead of 3D scene, set the sequencer as the render source. If a sequence editor with strips exists, Blender renders from VSE by default. To force it, ensure:
scene.render.use_sequencer = True
Fast iteration
Rendering full video is slow. Use the cheapest check that answers your question:
- Strip layout/timing — screenshot the VSE timeline, don't render
- Single frame check — render one frame at
resolution_percentage = 25 instead of the full animation
- Final output — full resolution animation
Batch changes before rendering. Don't render after every strip adjustment.
Workflow: subtitle overlay
scene = bpy.context.scene
se = scene.sequence_editor_create() if not scene.sequence_editor else scene.sequence_editor
video = se.strips.new_movie(name="Main", filepath="/path/to/video.mp4", channel=1, frame_start=1)
fps = scene.render.fps
subs = [
(0, 3, "Welcome to the video"),
(3, 6, "Let me show you something"),
(6, 10, "Thanks for watching!"),
]
for i, (start_sec, end_sec, text) in enumerate(subs):
s = se.strips.new_effect(
name=f"Sub_{i}", type="TEXT", channel=2,
frame_start=int(start_sec * fps) + 1,
length=int((end_sec - start_sec) * fps)
)
s.text = text
s.font_size = 48
s.location = (0.5, 0.1)
s.alignment_x = 'CENTER'
s.color = (1, 1, 1, 1)
s.use_shadow = True
s.use_box = True
s.box_color = (0, 0, 0, 0.6)
s.box_margin = 0.01
Switching to Video Editing workspace
The Video Editing workspace can be loaded from Blender's built-in template:
template_path = "/Applications/Blender.app/Contents/Resources/5.1/scripts/startup/bl_app_templates_system/Video_Editing/startup.blend"
bpy.ops.workspace.append_activate(idname="Video Editing", filepath=template_path)
bpy.context.window.workspace = bpy.data.workspaces["Video Editing"]
Important: The template's sequencer areas don't auto-link to the current scene's strips.
Each workspace has its own sequencer_scene (separate from window.scene).
Must be set with a timer delay after the workspace switch:
template_path = "/Applications/Blender.app/Contents/Resources/5.1/scripts/startup/bl_app_templates_system/Video_Editing/startup.blend"
bpy.ops.workspace.append_activate(idname="Video Editing", filepath=template_path)
bpy.context.window.workspace = bpy.data.workspaces["Video Editing"]
def fix_scene():
bpy.context.workspace.sequencer_scene = bpy.data.scenes["Scene"]
bpy.context.scene.frame_set(40)
return None
bpy.app.timers.register(fix_scene, first_interval=0.3)
Known issues (Blender 5.1)
- Strip modifiers crash:
strip.modifiers.new() segfaults. Avoid until patched.
- TRANSFORM effect type removed: use
strip.transform property instead.
- Rendering can crash with threading issues — use lower resolution_percentage for test renders.