R&D brief (references/design-motion-rnd.md): 2024-2026 design/motion trends, animating-anything craft, Iran-aware asset pipeline, masterpiece + platform playbook. New craft skills: motion-design-principles, scene-transitions, kinetic-typography, video-hooks, particles-and-effects, asset-sourcing — grounded in the Remotion stack. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
9.2 KiB
name, description
| name | description |
|---|---|
| asset-sourcing | How to source, license, AI-generate, prepare, and organize royalty-free assets (footage, images, textures, HDRIs, GLTF/GLB, icons, illustrations) for FlatRender Remotion templates — Iran-aware (geo-blocks), vendored-only, license-firewalled. Use when a template needs real media, when downloading/committing assets into public/, when grading/masking/looping footage or compositing it via Video/OffthreadVideo/Img/staticFile + Ken-Burns, or when generating bespoke assets with local AI models. |
Asset sourcing for templates
Project: services/remotion/. Helpers: src/lib/anim.ts (hexToRgba, mixHex, rand), src/lib/aspect.ts (useLayout → isWide/isSquare/isTall, vmin, unit), src/lib/branding.ts (colorSchema, BRAND), src/lib/fonts.ts (FONT = Vazirmatn, RTL), src/lib/three-kit.tsx (StudioEnv/Lights/Floor/Effects, Confetti3D). Render is headless Chrome in Docker — every value derives from useCurrentFrame() (never Math.random/Date.now/useFrame; use rand(i)).
The Iron Rule — vendor everything
The Iran environment punishes runtime dependencies. Download once (VPN if needed), commit into public/, reference with staticFile(). Never put https://… in a shipped template — a geo-block or flaky tunnel kills the render mid-frame. Mirror npm/NuGet/Docker via Nexus (mirror.soroushasadi.com); asset binaries are sourced by hand. Record the license at acquisition time, not later. public/ today holds only fonts/ — you build the rest.
License taxonomy (know cold — this is the firewall)
| Class | Examples | Ship? |
|---|---|---|
| CC0 / Public Domain / Pixabay / Pexels / Unsplash | Poly Haven, ambientCG, Kenney, Mixkit | ✅ free, no credit — default target |
| CC-BY | many Sketchfab, Bensound | ⚠️ ship only with a tracked on-screen/end-card credit |
| CC-BY-SA | some Wikimedia | ❌ share-alike can infect our proprietary template |
| CC-BY-NC | "free for personal" tiers | ❌ we are a paid product = commercial |
| Editorial / rights-managed | news/celebrity stock | ❌ |
| Paid stock | Envato, Adobe, Shutterstock | ✅ per license — keep the receipt/PDF |
No license row = unknown license = do not ship.
Sourcing map (CC0 / no-attribution first) + Iran access
| Type | Best CC0 sources | Commit to | Iran access |
|---|---|---|---|
| Footage (H.264 MP4, right-sized) | Pexels Video, Pixabay Video, Mixkit, Coverr, Videvo (filter CC0) | public/footage/{nature,business,abstract}/ |
Pixabay/Mixkit/Coverr OK; Pexels VPN-ish |
| Images | Pexels, Pixabay, Unsplash, StockSnap, Burst | public/images/ |
Pixabay OK; Pexels/Unsplash VPN-ish |
| Textures / overlays | Poly Haven, ambientCG; grain/light-leak/dust CC0 clips | public/textures/, public/overlays/ |
OK |
| HDRIs (1k–2k for render speed) | Poly Haven, ambientCG | public/hdri/ |
OK |
| 3D (prefer GLB over glTF+textures) | Poly Haven Models, Kenney, Khronos glTF samples, Sketchfab (check each) | public/models/ |
Poly Haven OK; Sketchfab VPN-ish |
| Icons (bundle via Nexus npm, never CDN) | Lucide, Tabler, Heroicons, Phosphor | npm dep | npm via Nexus OK |
| Illustrations (recolorable SVG) | unDraw, Open Peeps, Humaaans | public/illustrations/ |
OK |
For Persian/Iran imagery search English terms ("Tehran", "Iranian food") + self-shot/local stock. Sanction-blocked at account/payment: Adobe Stock, Envato — use a foreign account/partner or skip. Mitigation: do one batched "asset run" over a stable tunnel, commit binaries, render never touches the open internet again. Draco-compress GLBs (gltf-pipeline -i in.glb -o out.glb -d), keep low-poly for headless render speed.
AI-generated assets — when it's right
- Use when: the asset doesn't exist as stock (specific Persian cultural scene, branded mascot), you need consistency across a template set (reference-image control), or it beats a 5-site license hunt.
- Don't when: clean CC0 already exists, you need photographic authenticity, or a free tier's commercial license is unclear (watermarks / non-commercial = legal landmine for a paid product).
- Iran-pragmatic: self-host open models — HunyuanVideo 1.5 (~RTX 4090, no geo-block/payment/watermark) for video; FLUX/SDXL locally for image/texture/illustration. Hosted SaaS (Runway, Kling) only when local quality falls short and a VPN+foreign-account path exists. Always record prompt + tool + plan-tier + date in the asset's
.license.txtsidecar.
Preparing footage in Remotion (composite, grade, mask, loop)
Primitives: <OffthreadVideo> = default for all video in a render (FFmpeg extraction, deterministic, no seek drift). <Video> = preview only. <Img> over raw <img> (waits for load → no half-loaded frames). staticFile() for every vendored asset.
import { OffthreadVideo, Img, staticFile, useCurrentFrame, interpolate, Easing } from "remotion";
import { useLayout } from "./lib/aspect";
const frame = useCurrentFrame();
const L = useLayout();
// Ken-Burns: overscan ≥1 so no edges reveal; cover + center crops cleanly in all 3 aspects.
const scale = interpolate(frame, [0, 150], [1.08, 1.2], { extrapolateRight: "clamp" });
const ty = interpolate(frame, [0, 150], [0, L.vmin(-30)], { easing: Easing.out(Easing.cubic), extrapolateRight: "clamp" });
<OffthreadVideo
src={staticFile("footage/nature/forest-loop.mp4")}
style={{ width: "100%", height: "100%", objectFit: "cover",
transform: `scale(${scale}) translateY(${ty}px)`,
filter: "contrast(1.08) saturate(1.15) brightness(0.96)" }} // grade
/>
| Job | Pattern |
|---|---|
| Color grade | per-layer CSS filter (contrast/saturate/brightness/hue-rotate); build a shared lib/grades.ts (warm, teal-orange, mono, filmic) so palette can drive hue-rotate/saturate. Heavy grade → pre-grade in DaVinci Resolve (free), then commit. |
| Masking / keying | no native keyer — pre-key in Resolve/AE, export alpha (ProRes 4444 or WebM/VP9 alpha), then <OffthreadVideo>. Shape masks via CSS maskImage/clipPath + hexToRgba gradients, or SVG <mask>. |
| Seamless loop | source loop-designed clips (Coverr/Mixkit) or mirror-pingpong; <OffthreadVideo loop> once first/last frames match; crossfade-to-self with overlapping <Sequence> for imperfect footage. |
| Overlays (cheap "authentic" layer) | stack grayscale-on-black/white clips: screen for light-leaks/bokeh/dust, overlay/soft-light for grain, multiply for vignettes/paper. Keep palette-independent. Animated grain must move — offset background-position per frame or jitter SVG feTurbulence seed. |
| Per-aspect crop | objectFit:"cover" + center-safe framing; branch focal point on L.isWide/isSquare/isTall (or the proposed L.pick(wide,square,tall)) so the subject never crops out. |
HDRIs/GLBs: feed staticFile("hdri/…") into three-kit's StudioEnv; load models with useGLTF(staticFile("models/…glb")), idle-bob with Math.sin(frame/fps) (driven by useCurrentFrame, not useFrame).
Library structure + attribution firewall
Create under public/: footage/{nature,business,abstract}/, overlays/, images/, textures/, hdri/, models/, icons/, illustrations/, plus assets.json + ASSETS.md. Lowercase-kebab names, no spaces. Every asset gets one assets.json row at download time:
{ "file": "footage/nature/forest-loop.mp4", "source": "Pexels",
"url": "https://www.pexels.com/video/...", "author": "Name",
"license": "Pexels", "attribution_required": false, "commercial_ok": true,
"acquired": "2026-06-21", "notes": "1080p H.264, loops clean" }
Sidecar .license.txt next to AI assets (prompt + tool + date) and paid receipts. A CI validation script asserts every file in the media folders has a matching row with commercial_ok:true, else fails the build — this is the firewall. ASSETS.md is the generated human/legal-readable table. attribution_required:true must surface a credit on a shippable surface (end-card/footer). If the repo bloats, move large media to MinIO (already in stack) with a predeploy sync into public/ — but present at render time.
Checklist (before committing an asset / shipping a template)
- Vendored in
public/…and referenced viastaticFile()— no external URL anywhere in the template. assets.jsonrow added withcommercial_ok:true;.license.txt/receipt for AI/paid; CC-BY credits surfaced.- Right-sized (don't ship 4K into a 1080p comp); video is H.264 MP4 played via
<OffthreadVideo>; images via<Img>. - GLB (not glTF+loose textures), Draco-compressed, low-poly; HDRI 1k–2k.
- Footage graded through
lib/grades.ts; overlay grain/light-leak moves per frame; loops are seamless. - Ken-Burns overscans (start scale ≥ 1.05) and
objectFit:covercrops cleanly in 16:9 / 1:1 / 9:16 with subject in frame. - Re-render twice → identical (deterministic; nothing pulled from network/random/date).
Related: remotion-design-styles, remotion-template-composition, remotion-aspect-ratios, remotion-character-design, remotion-svg-colors, remotion-music-picker, remotion-sound-effects, persian-fonts, flatrender-template-seo.