Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ A CLI tool that renders motion design videos from JSON scenarios. No browser, no
[![docs.rs](https://docs.rs/rustmotion/badge.svg)](https://docs.rs/rustmotion)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

MIT-licensed: no licence key, no telemetry, no per-render billing. See [Non-goals](docs/non-goals.md) for this and everything else rustmotion deliberately doesn't do (no embeddable Player/browser/React, no vendor-cloud deploy target, ...).

## Install

```bash
Expand Down
12 changes: 6 additions & 6 deletions crates/rustmotion-core/tests/audit_ws_k.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! Regression tests for the workstream K (docs, schema, CI) audit findings:
//! documentation drift and inert schema fields.
//! RM-02, RM-47, RM-52.

use std::collections::BTreeSet;
use std::fs;
Expand Down Expand Up @@ -58,7 +58,7 @@ fn count_component_variants(lib_rs: &str) -> usize {
.count()
}

/// README.md and `rustmotion-components/Cargo.toml` (the text
/// RM-02 / RM-47: README.md and `rustmotion-components/Cargo.toml` (the text
/// crates.io shows for the published crate) both claimed "51 components"
/// while `Component` actually had 60 variants — and nothing kept the two in
/// sync. Locks the documented counts to the real one so a future component
Expand Down Expand Up @@ -103,14 +103,14 @@ fn documented_component_count_matches_enum_variant_count() {
const KNOWN_INERT_FIELDS: &[(&str, &str)] = &[
(
"codec",
" VideoConfig.codec is not threaded into the encode path. Honouring it means \
"RM-50: VideoConfig.codec is not threaded into the encode path. Honouring it means \
editing crates/rustmotion/src/cli/mod.rs (the Render/Batch/Still command handlers) and \
crates/rustmotion/src/encode/, both outside workstream K's owned files — see the \
workstream K report's handover for the exact wiring point.",
),
(
"intensity",
" MotionBlurConfig.intensity is read into AnimatedProperties.motion_blur \
"RM-51: MotionBlurConfig.intensity is read into AnimatedProperties.motion_blur \
(crates/rustmotion-core/src/engine/animator.rs:259) but nothing reads that field \
afterwards. Wiring it into the ghost-opacity math, or deleting it, both touch \
crates/rustmotion-components/src/box_builder.rs and crates/rustmotion-core/src/engine/\
Expand Down Expand Up @@ -192,8 +192,8 @@ fn extract_pub_field_names(source: &str) -> Vec<String> {
names
}

/// nothing stopped a schema field from parsing successfully and then
/// being read by no code path — (`VideoConfig.codec`) and
/// RM-52: nothing stopped a schema field from parsing successfully and then
/// being read by no code path — RM-50 (`VideoConfig.codec`) and RM-51
/// (`MotionBlurConfig.intensity`) are exactly that defect, and the format's
/// credibility as an LLM generation target rests on a field either doing
/// something or failing to parse. This test greps the workspace for a
Expand Down
37 changes: 37 additions & 0 deletions docs/non-goals.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Non-goals

rustmotion renders motion-design videos from JSON scenarios with a single Rust binary — Skia for drawing, taffy for layout, ffmpeg/openh264 for encoding. That focus rules out whole categories of feature that a browser-based or SaaS video tool would otherwise be expected to have. This page names them explicitly, so they're discoverable without reading source or asking.

## Browser, React, DOM

rustmotion has no browser and no JavaScript runtime anywhere in its render path — that absence is the whole point (see the README's opening line). Consequently, out of scope by construction:

- **An embeddable Player component.** There is no `<Player>` to drop into a React/Vue/Svelte app for in-browser preview. Preview happens by rendering a frame (`rustmotion render --frame N`) or a still (`rustmotion still`), or through the standalone `rustmotion-studio` desktop app.
- **A JS/webpack/Vite bundler pipeline.** Scenarios are JSON (or the HTML dialect that transpiles to JSON); there's no module graph to bundle.
- **Tailwind, SCSS, or any CSS preprocessor.** The `style` object is a flat, typed `CssStyle` deserialized by serde — no cascade resolution beyond the deliberately small color/font-* inheritance rustmotion itself implements, no class names, no preprocessing step.
- **three.js or any WebGL/WebGPU scene graph.** 3D transforms (`rotate_x`, `rotate_y`, `perspective`) are real Skia M44 matrix math on 2D layers, not a 3D renderer — there's no mesh, camera, or lighting model to speak of.
- **An in-browser visual studio.** `rustmotion-studio` is a native desktop app (Dioxus, its own binary), not a web app — there is no hosted or local-webserver editing UI.
- **`delayRender()` / async-render-blocking primitives.** rustmotion has no concept of a component signaling "not ready yet" mid-render, because there's no async component tree to block: all assets (images, fonts, icons) are resolved before a frame is painted, synchronously, by the loader.

## Commercial

**No licence key, no telemetry, no per-render billing.** rustmotion is MIT-licensed (see the badge at the top of the README) and that is a complete description of its commercial model — not "free while we build a paid tier." The binary makes exactly three categories of outbound network call, every one of them opt-in by virtue of what's in your scenario: fetching an Iconify icon by id, fetching a Google Font by family name (`FontEntry.source = "google"`), and resolving an `include` entry given as a URL instead of a local path. Neither call reports usage, render count, or any other telemetry, and nothing about this project's licensing or pricing is decided by what a scenario contains. This is worth stating plainly next to the MIT badge because an automated pipeline (CI job, agent, batch renderer) that shells out to `rustmotion render` should not need to reason about license enforcement, seat counts, or metered usage — there is none.

## Vendor cloud

rustmotion doesn't deploy anywhere, and doesn't know what "anywhere" means:

- No AWS Lambda / Google Cloud Run / Vercel deployment target, packaging, or adapter — nothing sizes the binary for a serverless runtime.
- No S3 (or any object-storage) output — `render`/`batch`/`still` write to a local path; shipping the result somewhere else is left to whatever calls the CLI.
- No IAM roles, cloud credentials, or cloud-specific configuration of any kind.
- No pricing model tied to render minutes, resolution, or concurrency — see [Commercial](#commercial) above.

If you need cloud rendering, `rustmotion render --frames a-b` plus `rustmotion concat` is the building block a distributed render pipeline is expected to be built *on top of* — rustmotion renders frames; orchestrating that across machines is out of scope.

## Known gaps (not exclusions)

These are real absences today, not deliberate non-goals — each is natively reachable given the existing dependency stack or architecture, and could reasonably be added without a redesign:

- **SkSL runtime effects.** `skia-safe` (the Skia binding rustmotion is built on) exposes `RuntimeEffect` for custom pixel shaders, and the workspace has zero uses of it today. A `style` field that compiles and runs an SkSL shader is plausible future work, not something the architecture excludes.
- **A public path toolkit.** Bezier/SVG-path math already exists internally (`arrow`'s curve fields, `connector`'s routing, `motion_path`'s SVG path parsing) but isn't exposed as a reusable, documented primitive a scenario author can compose with directly.
- **A seeded `random`/`noise` value.** `wiggle`'s procedural noise and `particle`'s seeded layouts are deterministic and reproducible *for those two components specifically*, but there's no general-purpose `{ "$random": { "seed": ..., "min": ..., "max": ... } }`-style value provider a scenario can use anywhere a number is expected.
Loading