Schema v1.0
Root element
<wity-scene version="1.0" width="1920" height="1080" dur="8.0">
...
</wity-scene>| Attribute | Type | Required | Description |
|---|---|---|---|
version | string | yes | Must be "1.0" |
width | number | yes | Canvas width in pixels |
height | number | yes | Canvas height in pixels |
dur | number | yes | Total duration in seconds |
Document children
The root <wity-scene> element accepts two types of direct children:
<ws-cast>— optional; contains<ws-character>metadata entities<ws-layer>— one or more visual/audio composition layers
Layers
<ws-layer id="title" z="10" opacity="1">
...
</ws-layer>| Attribute | Type | Default | Description |
|---|---|---|---|
id | string | — | Unique layer identifier |
z | number | 0 | Layer z-index (stacking order) |
opacity | number | 1 | Layer-level opacity (0–1), multiplied with element opacity |
Common element attributes
Visual elements (ws-text, ws-rect, ws-image, ws-video) share all of these. ws-audio shares only the temporal subset (id, begin, dur).
| Attribute | Type | Default | Description |
|---|---|---|---|
id | string | auto | Unique element identifier |
x | unit | 0 | Horizontal position |
y | unit | 0 | Vertical position |
anchor | enum | top-left | Origin point for x/y |
begin | number | 0 | Start time in seconds |
dur | number | ∞ | Duration in seconds |
z | number | 0 | Z-index within layer |
opacity | number | 1 | Element opacity (0–1) |
animate-in | enum | none | Entrance animation |
animate-out | enum | none | Exit animation |
animate-dur | number | 0.4 | Anim duration in seconds |
animate-easing | string | none | Custom cubic bezier for animate-in/out: "x1,y1,x2,y2" (same format as CSS cubic-bezier()) |
name | string | none | Optional human-readable display name |
Anchor values
top-left · top · top-right · left · center · right · bottom-left · bottom · bottom-right
Animation values
none · fade · fade-up · fade-down · slide-left · slide-right
Slide animations travel 40px. Default easing: easeOutCubic (entrance) / easeInCubic (exit). Use animate-easing="0.25,0.1,0.25,1" to override with a custom cubic bezier (applied to both directions).
Unit values
| Form | Example | Resolves to |
|---|---|---|
| percent | "50%" | 50% of canvas width or height |
| px | "120px" | 120px |
| bare | 120 | 120px |
<ws-text>
<ws-text
x="50%" y="40%" anchor="center"
font-size="5%" color="#ffffff" font-weight="bold"
animate-in="fade-up" animate-dur="0.6">
Opening Night
</ws-text>| Attribute | Type | Default |
|---|---|---|
font-size | unit | "3%" |
font-family | string | "sans-serif" |
font-weight | string | "normal" |
color | string | "#ffffff" |
text-align | enum | "center" |
line-height | number | 1.4 |
max-width | unit | none |
letter-spacing | unit | 0 |
Text content is the element's text node.
<ws-keyframe> (inside ws-text, ws-rect, ws-image)
Optional keyframe children animate position or opacity over element-relative time. Multiple <ws-keyframe> elements are sorted by t and interpolated linearly (or with a per-segment easing).
<ws-text x="10%" y="50%" anchor="center"
font-size="5%" color="#ffffff">
<ws-keyframe t="0" x="10%" opacity="0" />
<ws-keyframe t="1" x="50%" opacity="1" easing="0.25,0.1,0.25,1" />
<ws-keyframe t="4" x="50%" opacity="1" />
<ws-keyframe t="5" x="90%" opacity="0" />
Hello
</ws-text>| Attribute | Type | Required | Description |
|---|---|---|---|
t | number | yes | Element-relative time in seconds (from element's own begin, not scene time) |
x | unit | no | Horizontal position at this keyframe |
y | unit | no | Vertical position at this keyframe |
opacity | number | no | Opacity (0–1) at this keyframe |
easing | string | no | Cubic bezier for the segment FROM this keyframe to the next: "x1,y1,x2,y2" |
- Only properties specified in at least one keyframe are animated; others use the element's static attribute value.
- Keyframe
tis element-relative — it does not shift when you move an element on the timeline. - Easing is defined on the departing keyframe (CSS convention): the
easingon keyframe N controls the curve from N → N+1. <ws-keyframe>andanimate-in/animate-outcompose independently — animation presets still apply at the element boundaries.- Supported on
<ws-text>,<ws-rect>,<ws-image>. Not supported on<ws-video>or<ws-audio>.
<ws-rect>
<ws-rect x="0" y="0" width="100%" height="100%" fill="#000000" opacity="0.6" />| Attribute | Type | Default |
|---|---|---|
width | unit | "100%" |
height | unit | "100%" |
fill | string | "transparent" |
stroke | string | none |
stroke-width | number | 1 |
rx | number | 0 |
<ws-image>
<ws-image src="https://..." width="100%" height="100%" fit="cover" begin="2" dur="4" />| Attribute | Type | Default |
|---|---|---|
src | string | required |
width | unit | "100%" |
height | unit | "100%" |
fit | enum | "cover" |
fit values: cover · contain · fill · none
<ws-video>
A video clip element — positioned and temporally placed within a layer. Extends all common element attributes.
<ws-video
src="https://cdn.example.com/clip.mp4"
width="100%" height="100%" fit="cover"
begin="0" dur="8"
volume="0.8" trim-in="2.5">
<ws-cue begin="1.0" end="3.2" speaker="char1">Welcome back everyone</ws-cue>
</ws-video>| Attribute | Type | Default | Description |
|---|---|---|---|
src | string | required | Video file URL |
width | unit | "100%" | Display width |
height | unit | "100%" | Display height |
fit | enum | "cover" | Object-fit: cover · contain · fill · none |
volume | number | 1 | Playback volume 0–1 |
trim-in | number | 0 | Start offset within the source file (seconds) |
trim-out | number | none | End offset within the source file (seconds); omit = play to end |
muted | boolean | false | Mute audio track |
Both <ws-video> and <ws-audio> may contain optional <ws-cue> children (see below).
<ws-audio>
A temporal audio track element — lives inside a ws-layer but has no visual output and no spatial attributes.
<ws-audio
src="https://cdn.example.com/music.mp3"
begin="0" dur="30"
volume="0.4" loop="false">
<ws-cue begin="0" end="5.2">Intro melody</ws-cue>
</ws-audio>| Attribute | Type | Default | Description |
|---|---|---|---|
id | string | auto | Unique element identifier |
begin | number | 0 | Start time in seconds |
dur | number | ∞ | Duration in seconds |
src | string | required | Audio file URL |
volume | number | 1 | Playback volume 0–1 |
loop | boolean | false | Loop the audio |
trim-in | number | 0 | Start offset within the source file (seconds) |
trim-out | number | none | End offset within the source file (seconds); omit = play to end |
name | string | none | Optional human-readable display name |
<ws-cue>
Optional timed speech/subtitle cue, nested inside <ws-video> or <ws-audio>. Non-rendered metadata — consumed by analysis services, AI agents, and accessibility tools. Timestamps are relative to the source media file (aligned with trim-in/trim-out), not the scene timeline.
<ws-video src="interview.mp4" begin="0" dur="20" volume="1">
<ws-cue begin="1.2" end="3.5" speaker="char1">Welcome to the show</ws-cue>
<ws-cue begin="4.0" end="6.8" speaker="char2">Thanks for having me</ws-cue>
</ws-video>| Attribute | Type | Required | Description |
|---|---|---|---|
begin | number | yes | Start time within source media (seconds) |
end | number | yes | End time within source media (seconds) |
speaker | string | no | ws-character id (links to scene.cast) |
Text content is the cue's text node.
In the parsed object, cues are accessed as element.cues: WsCue[] (optional — absent when no cues are present).
<ws-cast> and <ws-character>
The optional <ws-cast> section contains semantic character entities. These are not rendered — they travel with the scene document and are consumed by authoring tools, AI agents, players, and compilers.
<ws-cast>
<ws-character id="char1" name="Sarah" role="Host"
description="Energetic, warm presenter"
avatar-url="https://cdn.example.com/sarah.jpg" />
<ws-character id="char2" name="Alex" role="Narrator" />
</ws-cast>| Attribute | Type | Required | Description |
|---|---|---|---|
id | string | yes | Unique character identifier |
name | string | yes | Display name |
role | string | no | Role in the scene (e.g. "Host", "Narrator") |
description | string | no | Free-form notes or personality description |
avatar-url | string | no | URL to avatar or reference image |
In the parsed WityScene object, characters are accessed as scene.cast: WsCharacter[].
Full example — product video scene
<?xml version="1.0" encoding="UTF-8"?>
<wity-scene version="1.0" width="1080" height="1920" dur="15.0">
<!-- Cast metadata (non-rendered) -->
<ws-cast>
<ws-character id="char1" name="Maya" role="Host"
description="Upbeat product presenter" />
</ws-cast>
<!-- Background video -->
<ws-layer id="video" z="0">
<ws-video src="https://cdn.example.com/bg-clip.mp4"
width="100%" height="100%" fit="cover"
begin="0" dur="15" volume="0" />
</ws-layer>
<!-- Music bed -->
<ws-layer id="audio" z="1">
<ws-audio src="https://cdn.example.com/music.mp3"
begin="0" dur="15" volume="0.35" />
</ws-layer>
<!-- Poster image — appears mid-scene -->
<ws-layer id="poster" z="2">
<ws-image src="https://cdn.example.com/product.jpg"
x="50%" y="45%" anchor="center"
width="80%" height="60%" fit="contain"
begin="4" dur="6"
animate-in="fade" animate-dur="0.5" />
</ws-layer>
<!-- Text overlays -->
<ws-layer id="graphics" z="10">
<ws-rect x="0" y="0" width="100%" height="100%"
fill="#000000" opacity="0.4"
begin="0" dur="3" animate-out="fade" animate-dur="0.5" />
<ws-text x="50%" y="38%" anchor="center"
font-size="7%" font-weight="bold" color="#ffffff"
animate-in="fade-up" animate-dur="0.6">
New Collection
</ws-text>
<ws-text x="50%" y="52%" anchor="center"
font-size="3%" color="#cccccc"
animate-in="fade" begin="0.4" animate-dur="0.5">
Spring 2026
</ws-text>
</ws-layer>
</wity-scene>