Version: 1.0
Last Updated: October 24, 2025
File Extension: .json
Font Animation Studio saves projects as JSON files containing all project data including text objects, animations, canvas settings, and font references. This format is designed to be human-readable and version-controlled.
{
"version": "1.0",
"textObjects": [...],
"settings": {...},
"fonts": [...]
}- Description: Format version identifier
- Current Value:
"1.0"
- Description: Array of all text objects in the project
- Type: Array of Text Object structures
- Default:
[](empty array for new projects)
- Description: Canvas and animation settings
- Type: Settings Object structure
- Description: List of font family names used in the project
- Type: Array of strings
- Usage: Used to detect missing fonts when loading projects
Each text object represents an animated text element on the canvas.
{
"id": 1697123456789,
"text": "Sample Text",
"fontFamily": "Arial",
"openTypeFeatures": {},
"initialState": {
"x": 100,
"y": 150,
"fontSize": 48,
"lineHeight": 1.2,
"color": "#000000"
},
"keyframes": {
"x": [...],
"y": [...],
"fontSize": [...],
"lineHeight": [...],
"color": [...],
"variableaxis:wght": [...],
"wdth": [...]
}
}| Property | Type | Required | Description | Default | Range/Format |
|---|---|---|---|---|---|
id |
number | ✅ | Unique identifier (timestamp) | Date.now() |
Positive integer |
text |
string | ✅ | Text content to display | "Sample Text" |
Any string, max ~500 chars |
fontFamily |
string | ✅ | Font family name | "Arial" |
Valid font name |
textAlign |
string | ✅ | Text alignment | "left" |
"left", "center", "right" |
openTypeFeatures |
object | ✅ | OpenType feature settings | {} |
See OpenType Features |
| Property | Type | Required | Description | Default | Range/Format |
|---|---|---|---|---|---|
initialState |
object | ✅ | Default property values before keyframes | {} |
See Initial State Object |
| Property | Type | Required | Description | Default | Range/Format |
|---|---|---|---|---|---|
keyframes |
object | ✅ | Property-specific keyframe arrays | {} |
See Keyframes Object |
Dynamic properties are organized by property name, each containing an array of keyframes:
{
"x": [
{ "frame": 0, "value": 100, "curve": {...} },
{ "frame": 60, "value": 400, "curve": {...} }
],
"y": [
{ "frame": 0, "value": 150 },
{ "frame": 60, "value": 300 }
],
"fontSize": [
{ "frame": 30, "value": 72 }
],
"variableaxis:wght": [
{ "frame": 0, "value": 400 },
{ "frame": 90, "value": 900 }
]
}The initialState object contains the default values for all animatable properties before any keyframes are created. This allows users to freely modify object properties without automatically creating keyframes, improving the editing experience.
{
"initialState": {
"x": 100,
"y": 150,
"fontSize": 48,
"lineHeight": 1.2,
"color": "#000000",
"variableaxis:wght": 400,
"variableaxis:wdth": 100
}
}Key Characteristics:
- Pre-keyframe state: Values used when no keyframes exist for a property
- Freely editable: Can be modified unlimited times without creating keyframes
- Per-property: Each animatable property can have an initial value
- Optional entries: Only properties that have been set need to be included
- Falls back to defaults: Missing properties use application defaults
Behavior:
- When an object is created, property changes update
initialState - No keyframes are created until the user explicitly adds them
- Once a property has keyframes,
initialStatefor that property is no longer used - Users can reset to initial state by removing all keyframes for a property
Variable font axes are now stored as individual keyframe arrays within the keyframes object. Each axis becomes its own animatable property:
{
"keyframes": {
"variableaxis:wght": [
{ "frame": 0, "value": 400 },
{ "frame": 60, "value": 900 }
],
"variableaxis:wdth": [
{ "frame": 30, "value": 75 },
{ "frame": 90, "value": 125 }
]
}
}- Key: Variable axis property name with
variableaxis:prefix (e.g.,"variableaxis:wght","variableaxis:wdth","variableaxis:opsz") - Value: Array of keyframe objects with frame/value pairs
Stores enabled OpenType features:
{
"liga": true,
"kern": true,
"ss01": false,
"smcp": true
}- Key: 4-character feature tag (e.g.,
"liga","kern","ss01") - Value: Boolean (true = enabled, false = disabled)
Individual keyframes define a single property value at a specific timeline position.
{
"frame": 60,
"value": 400,
"curve": {
"x1": 0.25,
"y1": 0.1,
"x2": 0.25,
"y2": 1.0
}
}| Property | Type | Required | Description | Default |
|---|---|---|---|---|
frame |
number | ✅ | Timeline frame number | 0 |
value |
any | ✅ | Property value at this frame | Property-dependent |
curve |
object | ❌ | Bezier curve for interpolation | Linear interpolation |
| Property | Type | Description | Range |
|---|---|---|---|
x |
number | Horizontal position | Any number |
y |
number | Vertical position | Any number |
fontSize |
number | Font size in pixels | 1-200 |
lineHeight |
number | Line height multiplier | 0.1+ |
color |
string | Text color | Hex color code |
variableaxis:{axis} |
number | Variable font axis (e.g., variableaxis:wght, variableaxis:wdth) |
Font-specific ranges |
Defines easing curve for animation interpolation:
| Property | Type | Description | Range |
|---|---|---|---|
x1 |
number | First control point X | 0.0-1.0 |
y1 |
number | First control point Y | 0.0-1.0 |
x2 |
number | Second control point X | 0.0-1.0 |
y2 |
number | Second control point Y | 0.0-1.0 |
Contains canvas and animation settings:
{
"canvasWidth": 1920,
"canvasHeight": 1080,
"canvasBackground": "#ffffff",
"frameRate": 30,
"duration": 5.0
}| Property | Type | Required | Description | Default | Range |
|---|---|---|---|---|---|
canvasWidth |
number | ✅ | Canvas width in pixels | 1920 |
100-8000 |
canvasHeight |
number | ✅ | Canvas height in pixels | 1080 |
100-8000 |
canvasBackground |
string | ✅ | Canvas background color | "#ffffff" |
Hex color code |
frameRate |
number | ✅ | Animation frame rate (fps) | 30 |
1-120 |
duration |
number | ✅ | Total duration in seconds | 5.0 |
0.1-300.0 |
{
"version": "1.0",
"textObjects": [
{
"id": 1697123456789,
"text": "Hello World",
"fontFamily": "Inter",
"openTypeFeatures": {
"liga": true,
"kern": true
},
"initialState": {
"x": 100,
"y": 100,
"fontSize": 48,
"color": "#ff0000",
"variableaxis:wght": 600
},
"keyframes": {
"x": [
{ "frame": 0, "value": 100 },
{
"frame": 150,
"value": 500,
"curve": {
"x1": 0.25,
"y1": 0.1,
"x2": 0.25,
"y2": 1.0
}
}
],
"y": [
{ "frame": 0, "value": 100 },
{ "frame": 150, "value": 300 }
],
"fontSize": [
{ "frame": 0, "value": 48 },
{ "frame": 150, "value": 72 }
],
"color": [
{ "frame": 0, "value": "#ff0000" },
{ "frame": 150, "value": "#0000ff" }
],
"variableaxis:wght": [
{ "frame": 0, "value": 600 },
{ "frame": 150, "value": 900 }
]
}
}
],
"settings": {
"canvasWidth": 1920,
"canvasHeight": 1080,
"canvasBackground": "#ffffff",
"frameRate": 30,
"duration": 5.0
},
"fonts": ["Inter", "Arial"]
}When a project is loaded with fonts that aren't available in the system:
- A warning modal displays missing font names
- User can choose to continue anyway
- Missing fonts fall back to system defaults (usually Arial)
- Font references remain in the data for when fonts become available
- Invalid JSON shows error notification
- Missing required fields use default values
- Invalid property values are clamped to valid ranges
- Malformed keyframes are skipped with warnings
When properties are changed via UI:
- Direct object properties are updated
- If no keyframes exist for the property,
initialStateis updated - If keyframes exist, current frame keyframe is updated/created
- Changes are reflected immediately in rendering
- Linear interpolation is used when no curve is specified
- Bezier curves use standard cubic-bezier interpolation
- Properties interpolate independently
Properties are validated on load:
- Numbers are clamped to valid ranges
- Colors default to black if invalid
- Missing required fields use defaults
- Arrays/objects are initialized if missing