Vivid is an elegant GUI for Video Frame Interpolation, Video Upscaling, and Video Restoration powered by AI. Built with React/TypeScript and Tauri (Rust) for a lightweight, fast, and secure desktop experience. It ships with a pre-packaged runtime — no Docker, WSL, or manual Python setup required. Download vivid here
- custom plugin scripts (
effect/inference), - community model architectures ("model packs"),
- backend extensions,
- host-agnostic runtime orchestration hooks.
The goal is simple: contributors can ship model logic without patching host internals.
Scaffold either extension type:
python3 scripts/scaffold_extension.py --kind plugin --id my-effect --name "My Effect" --output-dir /tmp/my-effect
python3 scripts/scaffold_extension.py --kind model-pack --id myarch --output-dir /tmp/myarch-packWhat you get:
plugin:manifest.json+main.pywith a frame-processor starter.model-pack: a registerableCommunityModelLogicBaseimplementation.
This SDK supports the same practical runtime mix most video AI authors need (ONNX and PyTorch with CPU/CUDA/MPS fallback) through stable helper APIs:
resolve_torch_device(backend_name)for backend-aware CPU/CUDA/MPS device selection,resolve_model_repo(...)/ensure_model_repo_on_path(...)for host-managed model-repo imports,ModelArtifactSpec+CommunityModelLogicBase.resolve_from_specs(...)for flexible artifact discovery.
These helpers are intentionally small and composable so external packs follow the same execution style as built-in engines/effects.
Use frame-processor mode unless you need direct VapourSynth graph control:
import numpy as np
from vivid_inference_core import resolve_torch_device
def init_plugin(config: dict):
params = config.get("plugin_params", {})
return {
"device": resolve_torch_device(config.get("backend")),
"strength": float(params.get("strength", 0.5)),
}
def process_frame(frame_hwc: np.ndarray, n: int, config: dict, state: dict) -> np.ndarray:
_ = n
_ = config
strength = max(0.0, min(1.0, float(state.get("strength", 0.5))))
return (frame_hwc * (1.0 - 0.1 * strength)).astype(np.float32, copy=False)Runtime contract:
- Input/output is HWC float array.
- Keep frame count/timeline stable unless intentionally changing it.
- Log to stderr (stdout is reserved for video stream output).
from vivid_inference_core import (
CommunityModelLogicBase,
ModelArtifactSpec,
register_model_pack,
resolve_model_repo,
)
class MyArch(CommunityModelLogicBase):
PYTORCH_NATIVE = True
def process(self, clip, config, backend, model_path):
_ = backend
_ = config
_ = model_path
return clip
def resolve_myarch_artifact(model_name: str | None, config_data: dict, current_dir: str) -> str | None:
_ = config_data
stem = model_name or "myarch-v1"
model_repo_root = resolve_model_repo("myarch", current_dir=current_dir)
model_repo_paths = [model_repo_root] if model_repo_root else []
specs = [
ModelArtifactSpec(
search_roots=[
f"{current_dir}/../models/community-myarch",
*model_repo_paths,
],
file_stems=[stem],
)
]
return CommunityModelLogicBase.resolve_from_specs(specs)
register_model_pack(
aliases=["myarch"],
model_logic=MyArch,
artifact_resolver=resolve_myarch_artifact,
)Add entries to catalog/community_catalog.json (validated by catalog/community_catalog.schema.json):
{
"version": 1,
"engines": [
{
"operation": "interpolation",
"engine": "community:myarch",
"label": "Community - MyArch",
"description": "Motion-aware temporal interpolation",
"backends": ["cpu", "pytorch-cuda"],
"models": [
{ "value": "myarch-v1", "label": "V1 Standard", "recommended": true },
{ "value": "myarch-v2-lite", "label": "V2 Lite" }
]
}
]
}The label fields you set here are the exact text users see in the Vivid UI:
| Field | Where it appears |
|---|---|
Engine label |
Algorithm picker dropdown, queue card header, processing page |
Engine description |
Tooltip/subtitle in the algorithm picker |
Model label |
Model dropdown, queue card model line |
Vivid resolves all display names through a single catalog-first lookup chain (src/renderer/utils/displayNames.ts). Your catalog labels take priority over any filename-based fallback, so you have full control over what the user sees.
Naming tips:
- Engine label: Use the full architecture name — e.g.
"Community - MyArch"or"MyArch - Temporal SR". This is shown in the algorithm picker. - Model label: Keep it short and don't repeat the engine name — the engine is displayed separately. Use
"V2 Lite"instead of"MyArch V2 Lite". The UI strips the engine prefix automatically, but clean labels from the catalog are always preferred.
vivid-core/
├── src/vivid_inference_core/
│ ├── __init__.py # CONTRACT_VERSION, run_pipeline, top-level re-exports
│ ├── contracts.py # Canonical contracts (BackendFactoryProtocol, ModelLogicProtocol, etc.)
│ ├── community_registry.py # resolve_model_logic, resolve_model_artifact, create_backend, register_model_pack
│ ├── plugin_runner.py # _entrypoint (frame-processor + graph-mode dispatch)
│ ├── authoring.py # CommunityModelLogicBase, ModelArtifactSpec, EngineCapabilityContract
│ └── helpers.py # resolve_torch_device, resolve_model_repo, ensure_model_repo_on_path
├── templates/plugin-pytorch/ # Starter template for PyTorch plugins
├── scripts/scaffold_extension.py # CLI scaffolding tool
└── catalog/
├── community_catalog.json # Community engine/model entries (consumed by UI)
└── community_catalog.schema.json # JSON Schema for the above
- Contracts:
contracts.py - Runner:
plugin_runner.py - Registry:
community_registry.py - Runtime orchestrator:
run_pipeline(...) - Authoring helpers:
authoring.py - Device/repo helpers:
helpers.py
- Vivid resolves
community:<slug>engines and dispatches through this package. - Custom plugins are local execution only.
- Private product concerns (auth, licensing, cloud policy, billing) stay out of scope.
0.x: API stabilization phase.1.x: semver + breaking-change discipline on public contracts.
- Community: AGPL-3.0-only (
LICENSE) - Commercial exception:
LICENSE-COMMERCIAL.md