shader-gen
GitHubWebGL视频特效、转场、蒙版及调色滤镜的AI着色器生成技能。遵循目录优先原则,先检索内置资产,再调用后端生成任务,并支持通过编辑项应用效果参数。
Trigger Scenarios
Install
npx skills add ChatCut-Inc/agent-plugin --skill shader-gen -g -y
SKILL.md
Frontmatter
{
"name": "shader-gen",
"description": "AI shader generator for WebGL video effects, transitions, masks, and color grading (LUT \/ 调色 \/ 电影感 \/ film look). Use when the user wants a video effect (滤镜 \/ 特效), a transition (转场 \/ crossfade \/ wipe \/ cube \/ 3d), a mask (蒙版 \/ 遮罩 \/ reveal), a zoom \/ push-in (推近 \/ 推镜头), or a color grade — try the built-in effects (zoom, builtin LUTs) before generating a new shader.\n",
"user-invocable": true
}
Shader Generator
Submit-only: creates a backend generation job, returns jobId. Use the track_progress tool for job lifecycle after submission.
Always use generate.ts for new shaders. Manual authoring is only for editing existing asset code — never as a fallback when generation fails.
Catalog-first rule — try existing assets before generation
Before generating a shader, call browse_library unless the user names an exact asset id that is already visible in read_project.
browse_library is the source of truth for built-in effects, built-in transitions, and project effect/transition assets. Built-ins are stable global asset ids, not per-project DB assets, so they may not appear in read_project asset lists.
Apply catalog entries with edit_item, do not call submit_shader.
Good catalog searches:
browse_library(query: "zoom")
browse_library(category: "transitions", query: "dissolve")
browse_library(category: "audio-fx")
Generate only when no catalog entry matches the user's intent closely enough.
builtin:zoom uses the same track-bound placement as every effect
Effects always have timeline geometry. For a whole-clip effect, pass targetItemId; ChatCut resolves it to a clip-anchored range covering that clip. For an explicit timeline range, pass trackId + trackBoundFrom + trackBoundDurationInFrames.
# Zoom on the entire video clip
edit_item(json: '{"adds":[{"type":"effect","assetId":"builtin:zoom","targetItemId":"<clip-id>","propertyOverrides":{"magnification":1.5,"shape":"hold"}}]}')
# Zoom on a sub-range of the clip (e.g. frames 90–150 only, a punch zoom on a beat)
edit_item(json: '{"adds":[{"type":"effect","assetId":"builtin:zoom","mode":"track-bound","trackId":"<trackId>","trackBoundFrom":90,"trackBoundDurationInFrames":60,"propertyOverrides":{"magnification":2,"shape":"punch"}}]}')
Use read_project({view:"timeline",track:"V1"}) to obtain track item ids and timeline-frame ranges. Use read_project({itemId:"..."}) when exact item detail is needed.
| Key | Type | Range / values | Default | Notes |
|---|---|---|---|---|
magnification |
number | 1–4 | 1.5 |
Zoom factor; 1 = no zoom, 2 = 2× in |
focalPointX |
number | 0–1 | 0.5 |
Horizontal focal point (0 = left, 1 = right) |
focalPointY |
number | 0–1 | 0.5 |
Vertical focal point (0 = top, 1 = bottom) |
shape |
select | punch / hold / slow-push / instant |
hold |
Animation curve |
focalMode |
select | auto / manual |
auto |
auto picks subject; manual uses focalPoint |
easeInFrames |
number | 0–60 | 8 |
Frames to ramp in |
easeOutFrames |
number | 0–60 | 8 |
Frames to ramp out |
Omit propertyOverrides entirely for default zoom. Send only the keys you want to change — patch semantics.
Clip-anchored vs adjustment-track
Effect items have two placements, both with a concrete time range:
- Clip-anchored: pass
targetItemIdfor the whole clip, or include an explicit range that intersects the clip. The stored range is local to that clip and follows it when it moves. - Adjustment-track: pass
trackId+trackBoundFrom+trackBoundDurationInFramesfor a range over empty track space. The range is absolute on the timeline.
Use targetItemId for whole-clip effects. Use explicit track geometry only when the requested range differs from a clip's full duration.
Built-in LUT properties
edit_item(json: '{"adds":[{"type":"effect","targetItemId":"<clip-id>","assetId":"builtin:slog3-s709","propertyOverrides":{"intensity":1}}]}')
| Key | Type | Range | Default | Notes |
|---|---|---|---|---|
intensity |
number | 0–1 | 1 |
LUT strength; 1 = full applied |
To swap: delete the effect and re-add with a different assetId. To remove: delete the effect item.
These are separate from user-uploaded .cube LUT assets (see "Applying an Existing LUT Asset" below) — those use a different code path with assetId:"lut".
Beta Status Gate
New shader generation is beta. Before generating, warn the user and wait for explicit confirmation.
Use the user's language. Chinese: "新的特效/转场生成目前还是 beta 阶段,可能会有不稳定的问题。如果你坚持要做,我可以帮你实现。" Skip if user already acknowledged in the same request.
Supported Targets
Effects and transitions apply to video, image, and gif items.
Type Routing
Before generating anything, check two non-generation paths first:
- Catalog entry — use
browse_libraryfor built-in and project effects/transitions. - User-uploaded
.cubeLUT asset that already exists in the project library — separate code path, see "Applying an Existing LUT Asset" below. The asset shows up inread_projectwithtype: lut.
| User wants | --type |
|---|---|
| Video appearance (color, blur, glow, grain, distortion) | effect |
| Color grade / look (teal-orange, cinematic, vintage, LUT-style) | effect |
| Visibility control (mask, reveal, wipe, shape cutout, gradient fade) | effect |
| Blend between clips (crossfade, dissolve, slide, 3D cube/page flip) | transition |
"LUT-style" in the table means generating a fresh GLSL color grade that resembles a LUT — only when the user wants something new. If they want to apply a .cube file already in the library, don't generate; bind the existing asset instead.
No separate LUT or mask generator for the generation path — those are all effect.
Applying an Existing LUT Asset
.cube files uploaded by the user become lut assets. Applying one to a clip is not generation — it's a single edit_item call that attaches an effect item whose assetId is the literal string "lut" and whose propertyOverrides.lut binds the real LUT asset id. (Legacy contract; the unified LUT API binds the LUT effect asset id directly — see edit_item description.)
edit_item(json: '{"adds":[{"type":"effect","targetItemId":"<clip-id>","assetId":"lut","propertyOverrides":{"intensity":1,"lut":{"assetId":"<lut-asset-id>","assetType":"lut","type":"asset"}}}]}')
Key points:
assetIdis the literal string"lut", not the LUT asset's id. The real LUT asset id goes insidepropertyOverrides.lut.assetId.intensityis 0–1; default 1 (full strength).targetItemTypedefaults tovideo; also supportsimage,gif.- To swap a LUT on an existing effect: update
propertyOverrides.lut.assetIdto the new LUT asset id. - To remove: delete the effect item.
Do not call generate.ts for this path. Do not pass a real LUT asset id as assetId — the editor checks assetId === "lut" to route into the LUT renderer; passing a UUID silently renders nothing.
Usage
Before calling submit_shader, restate the user's intent in one concrete sentence, then proceed immediately. After track_progress returns, state what was produced in one line — do NOT ask "要保留还是重新生成".
submit_shader({
type: "effect",
prompt: "Chromatic aberration with RGB split",
name: "Chromatic Aberration",
});
submit_shader({
type: "transition",
prompt: "Smooth crossfade with soft edge",
name: "Crossfade",
});
submit_shader({
type: "effect",
prompt: "Cinematic teal-orange color grade",
});
submit_shader({
type: "effect",
prompt: "Stronger version",
referenceAssetIds: ["effect_asset_id"],
});
Strategy
- Submit, then stop. Tell user the job was created.
- Use the
track_progresstool for status/wait after submission. - Generation always produces a library asset — never refuse because the timeline isn't ready.
- Apply is separate and optional. Only apply when user explicitly asks ("加到视频", "apply", "用到第一段"). When ambiguous, default to library-only.
Editing Existing Properties
Any time you're about to edit shader asset.properties, applied effect/transition item.propertyOverrides, or promote a hardcoded shader value, read references/property-changes.md first.
It reinforces that shader properties is an array, but the allowed shader property types are only number, boolean, color, select, and vec2. Motion Graphic properties are also arrays, but use a different type set.
Parameters
| Param | Description | Default |
|---|---|---|
type |
"effect" or "transition" (req'd) |
— |
prompt |
Description of the shader (req'd) | — |
name |
Asset name shown in library | — |
referenceAssetIds |
Asset ids. Image id → model LOOKS AT it for visual inspiration. Effect/transition id → reuse its code as style anchor (≤1 per submit, kind must match type). |
— |
Output
Returns { success, job: { jobId, status }, manage: { status, wait, watch } }.
Applying to Timeline
Only when user explicitly requests. Refresh the affected timeline with view:"timeline", passing track to narrow the read when appropriate.
Effect
edit_item(json: '{"adds":[{"type":"effect","targetItemId":"<id>","assetId":"<id>","enabled":true,"propertyOverrides":{}}]}')
Transition
Requires two adjacent same-track endpoints. edit_item validates live seam feasibility and refuses durations that would require freeze frames or overlapping neighboring transitions. If the add fails, retry with the suggested durationInFrames, trim the clips to expose handles, delete/shorten neighboring transitions, or keep a hard cut.
edit_item(json: '{"adds":[{"type":"transition","assetId":"<id>","outgoingItemId":"<id1>","incomingItemId":"<id2>","durationInFrames":30}]}')
Validation & Verification
Backend Validation
When generating via generate.ts, the backend handles validation automatically (transpile, AST security, class structure, retry on failure).
Manual Code Verification
NEVER write shader code from scratch. Always use generate.ts for new shaders. This section is ONLY for modifying existing shader code that was already generated.
When writing shader code manually, read ${CLAUDE_SKILL_DIR}/references/design-principles.md first. If the change touches editable properties, also read ${CLAUDE_SKILL_DIR}/references/property-changes.md.
Typical workflow:
inspect_assetwith the shaderassetIdandcode: true— read the current source.- Edit the source in your own context.
edit_assetwithaction=update, the sameassetId, and the full replacement source inline injson.code. Validation runs automatically on update — if code is invalid, the update is rejected with error details.
Version History
-
e1867a8
Current 2026-08-02 23:37
修正了builtin:zoom效果的适用模式说明,明确其支持track-bound和item-bound两种模式,而非仅限track-bound。
-
f39cdae
2026-07-30 21:54
同步插件版本至66c0309a40866ab98e1147b05d4cc160094efab5
- 5e9afe0 2026-07-22 10:58


