From 6f86fb47b0ca71a24a654f897a82ae88bddf30c0 Mon Sep 17 00:00:00 2001 From: Baptiste Parmantier Date: Tue, 22 Sep 2026 11:14:07 +0200 Subject: [PATCH] docs(non-goals): state what rustmotion will not do Refs #220 --- README.md | 2 ++ crates/rustmotion-core/tests/audit_ws_k.rs | 12 +++---- docs/non-goals.md | 37 ++++++++++++++++++++++ 3 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 docs/non-goals.md diff --git a/README.md b/README.md index f1702e11..c119f1d9 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/crates/rustmotion-core/tests/audit_ws_k.rs b/crates/rustmotion-core/tests/audit_ws_k.rs index 59a1504a..c6a68899 100644 --- a/crates/rustmotion-core/tests/audit_ws_k.rs +++ b/crates/rustmotion-core/tests/audit_ws_k.rs @@ -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; @@ -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 @@ -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/\ @@ -192,8 +192,8 @@ fn extract_pub_field_names(source: &str) -> Vec { 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 diff --git a/docs/non-goals.md b/docs/non-goals.md new file mode 100644 index 00000000..af98e175 --- /dev/null +++ b/docs/non-goals.md @@ -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 `` 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.