@litfamily/litmuse 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/README.md +20 -0
- package/README_ko-KR.md +20 -0
- package/bin/litmuse.mjs +4 -0
- package/lib/stage-card.mjs +180 -5
- package/package.json +1 -1
- package/plugin/.muse-plugin/plugin.json +26 -11
- package/plugin/commands/lit-status.md +2 -1
- package/plugin/commands/lit.md +1 -1
- package/plugin/hooks/lib/digest.mjs +1 -1
- package/plugin/hooks/lib/next-step.mjs +68 -0
- package/plugin/hooks/lib/routes.mjs +59 -0
- package/plugin/hooks/lib/workflow-stack.mjs +318 -0
- package/plugin/hooks/session-start.mjs +29 -2
- package/plugin/hooks/user-prompt-submit.mjs +138 -7
- package/plugin/lib/capped-fetch.mjs +25 -0
- package/plugin/lib/pinned-fetch.mjs +79 -0
- package/plugin/lib/plan-task-rows.mjs +32 -0
- package/plugin/lib/render_pages.py +29 -1
- package/plugin/lib/rewind/collect.mjs +679 -0
- package/plugin/lib/rewind/gauge.mjs +72 -0
- package/plugin/lib/rewind/next-step.mjs +91 -0
- package/plugin/lib/rewind/redact.mjs +83 -0
- package/plugin/lib/rewind/transcripts.mjs +227 -0
- package/plugin/skills/frontend-ui-ux/SKILL.md +24 -9
- package/plugin/skills/frontend-ui-ux/kit/components.css +414 -0
- package/plugin/skills/frontend-ui-ux/kit/fonts.json +22 -0
- package/plugin/skills/frontend-ui-ux/kit/layouts/landing.html +164 -0
- package/plugin/skills/frontend-ui-ux/kit/layouts/settings.html +114 -0
- package/plugin/skills/frontend-ui-ux/kit/layouts/workspace.html +115 -0
- package/plugin/skills/frontend-ui-ux/kit/styles/computational-brutalism.json +227 -0
- package/plugin/skills/frontend-ui-ux/kit/styles/field-notes.json +205 -0
- package/plugin/skills/frontend-ui-ux/kit/styles/graphite.json +200 -0
- package/plugin/skills/frontend-ui-ux/kit/styles/meadow.json +199 -0
- package/plugin/skills/frontend-ui-ux/kit/styles/neo-brutalism.json +222 -0
- package/plugin/skills/frontend-ui-ux/kit/ui.js +226 -0
- package/plugin/skills/frontend-ui-ux/references/complete-contract.md +2 -1
- package/plugin/skills/frontend-ui-ux/references/craft-floor.md +9 -4
- package/plugin/skills/frontend-ui-ux/references/default-editorial-pixel.json +59 -7
- package/plugin/skills/frontend-ui-ux/references/house-styles.md +234 -0
- package/plugin/skills/frontend-ui-ux/references/motion-guide.md +3 -3
- package/plugin/skills/frontend-ui-ux/references/slop-register.md +7 -7
- package/plugin/skills/frontend-ui-ux/scripts/design-lock.mjs +400 -0
- package/plugin/skills/frontend-ui-ux/scripts/interface-probe-lock.mjs +196 -0
- package/plugin/skills/frontend-ui-ux/scripts/interface-probe-page.mjs +31 -10
- package/plugin/skills/frontend-ui-ux/scripts/interface-probe-rules.mjs +19 -7
- package/plugin/skills/frontend-ui-ux/scripts/interface-probe-static.mjs +4 -0
- package/plugin/skills/frontend-ui-ux/scripts/interface-probe.mjs +59 -7
- package/plugin/skills/frontend-ui-ux/scripts/pixel-art.mjs +60 -0
- package/plugin/skills/lit-adhd/SKILL.md +188 -0
- package/plugin/skills/lit-adhd/references/card-examples.md +121 -0
- package/plugin/skills/lit-adhd/references/status-taxonomy.md +43 -0
- package/plugin/skills/lit-adhd/scripts/rewind.mjs +11 -0
- package/plugin/skills/lit-diagram-drawer/SKILL.md +3 -3
- package/plugin/skills/lit-diagram-drawer/scripts/diagram-craft-rules.mjs +1 -2
- package/plugin/skills/lit-diagram-drawer/scripts/visual-quality.mjs +0 -1
- package/plugin/skills/lit-docx/SKILL.md +1 -1
- package/plugin/skills/lit-hijack/SKILL.md +589 -0
- package/plugin/skills/lit-hijack/references/capture-and-provenance.md +123 -0
- package/plugin/skills/lit-hijack/references/clean-room-build.md +133 -0
- package/plugin/skills/lit-hijack/references/distil-and-licence.md +128 -0
- package/plugin/skills/lit-hijack/references/gap-and-port-design.md +152 -0
- package/plugin/skills/lit-hijack/references/parity-and-report.md +133 -0
- package/plugin/skills/lit-hijack/scripts/hijack.mjs +879 -0
- package/plugin/skills/lit-pptx/SKILL.md +20 -9
- package/plugin/skills/lit-pptx/fonts/jetbrains-mono/JetBrainsMono-Bold.ttf +0 -0
- package/plugin/skills/lit-pptx/fonts/jetbrains-mono/JetBrainsMono-Regular.ttf +0 -0
- package/plugin/skills/lit-pptx/fonts/jetbrains-mono/OFL.txt +93 -0
- package/plugin/skills/lit-pptx/fonts/provenance.json +32 -1
- package/plugin/skills/lit-pptx/references/direction-step.md +19 -2
- package/plugin/skills/lit-pptx/references/examples/13-engineering-review-computational-brutalism-ko.md +173 -0
- package/plugin/skills/lit-pptx/references/examples/14-launch-pitch-neo-brutalism-ko.md +154 -0
- package/plugin/skills/lit-pptx/references/examples/15-design-lecture-bauhaus-ko.md +139 -0
- package/plugin/skills/lit-pptx/references/examples/16-community-programme-riso-ko.md +158 -0
- package/plugin/skills/lit-pptx/references/examples/17-public-data-briefing-newsroom-ko.md +179 -0
- package/plugin/skills/lit-pptx/references/tonalities/bauhaus.md +112 -0
- package/plugin/skills/lit-pptx/references/tonalities/computational-brutalism.md +118 -0
- package/plugin/skills/lit-pptx/references/tonalities/neo-brutalism.md +125 -0
- package/plugin/skills/lit-pptx/references/tonalities/newsroom.md +115 -0
- package/plugin/skills/lit-pptx/references/tonalities/riso.md +109 -0
- package/plugin/skills/lit-pptx/scripts/compile-deck.js +1 -1
- package/plugin/skills/lit-pptx/scripts/craft_extras.py +136 -92
- package/plugin/skills/lit-pptx/scripts/embed_fonts.py +13 -1
- package/plugin/skills/lit-pptx/scripts/lib/grid-resolver.js +105 -116
- package/plugin/skills/lit-pptx/scripts/lib/layout-resolver.js +9 -15
- package/plugin/skills/lit-pptx/scripts/lib/render-pack.js +368 -561
- package/plugin/skills/lit-pptx/scripts/lib/slide-grid.js +104 -0
- package/plugin/skills/lit-pptx/scripts/lib/template-registry.js +57 -6
- package/plugin/skills/lit-pptx/scripts/qa_deck.py +4 -4
- package/plugin/skills/lit-pptx/templates/tonalities/atlas/pack.yaml +1 -1
- package/plugin/skills/lit-pptx/templates/tonalities/bauhaus/pack.yaml +98 -0
- package/plugin/skills/lit-pptx/templates/tonalities/chalk/pack.yaml +1 -1
- package/plugin/skills/lit-pptx/templates/tonalities/computational-brutalism/pack.yaml +99 -0
- package/plugin/skills/lit-pptx/templates/tonalities/gazette/pack.yaml +1 -1
- package/plugin/skills/lit-pptx/templates/tonalities/ledger/pack.yaml +1 -1
- package/plugin/skills/lit-pptx/templates/tonalities/neo-brutalism/pack.yaml +107 -0
- package/plugin/skills/lit-pptx/templates/tonalities/newsroom/pack.yaml +99 -0
- package/plugin/skills/lit-pptx/templates/tonalities/night/pack.yaml +1 -1
- package/plugin/skills/lit-pptx/templates/tonalities/paper/pack.yaml +1 -1
- package/plugin/skills/lit-pptx/templates/tonalities/riso/pack.yaml +99 -0
- package/plugin/skills/lit-pptx/templates/tonalities/signal/pack.yaml +2 -2
- package/plugin/skills/lit-pptx/templates/tonalities/studio/pack.yaml +1 -1
- package/plugin/skills/lit-recap/SKILL.md +3 -0
- package/plugin/skills/lit-schematic/SKILL.md +241 -0
- package/plugin/skills/lit-schematic/examples/iso-heat-exchanger/build.mjs +14 -0
- package/plugin/skills/lit-schematic/examples/iso-heat-exchanger/scene.mjs +8 -0
- package/plugin/skills/lit-schematic/examples/journal-adsorption-rig/build.mjs +22 -0
- package/plugin/skills/lit-schematic/examples/pid-dosing-skid/build.mjs +73 -0
- package/plugin/skills/lit-schematic/references/cutaway-outputs.md +25 -0
- package/plugin/skills/lit-schematic/references/isometric-craft.md +24 -0
- package/plugin/skills/lit-schematic/references/journal-figures.md +19 -0
- package/plugin/skills/lit-schematic/references/kit-api.md +27 -0
- package/plugin/skills/lit-schematic/references/pfd-and-layout.md +20 -0
- package/plugin/skills/lit-schematic/references/pid-drafting.md +26 -0
- package/plugin/skills/lit-schematic/references/pid-symbols.md +27 -0
- package/plugin/skills/lit-schematic/references/verification.md +24 -0
- package/plugin/skills/lit-schematic/scripts/doctor.mjs +13 -0
- package/plugin/skills/lit-schematic/scripts/lib/figure.mjs +49 -0
- package/plugin/skills/lit-schematic/scripts/lib/isa.mjs +18 -0
- package/plugin/skills/lit-schematic/scripts/lib/iso.mjs +58 -0
- package/plugin/skills/lit-schematic/scripts/lib/legend-sheet.mjs +10 -0
- package/plugin/skills/lit-schematic/scripts/lib/qa-core.mjs +241 -0
- package/plugin/skills/lit-schematic/scripts/lib/sheet.mjs +446 -0
- package/plugin/skills/lit-schematic/scripts/lib/svg.mjs +39 -0
- package/plugin/skills/lit-schematic/scripts/lib/theme.mjs +25 -0
- package/plugin/skills/lit-schematic/scripts/motion.mjs +8 -0
- package/plugin/skills/lit-schematic/scripts/qa.mjs +21 -0
- package/plugin/skills/lit-schematic/scripts/render.mjs +38 -0
- package/plugin/skills/lit-schematic/scripts/scaffold.mjs +73 -0
- package/plugin/skills/lit-scientific-visualization/SKILL.md +2 -0
- package/plugin/skills/lit-typographic-motion/SKILL.md +12 -8
- package/plugin/skills/lit-typographic-motion/engine/THIRD_PARTY_NOTICES +11 -0
- package/plugin/skills/lit-typographic-motion/engine/blender/runner.py +218 -0
- package/plugin/skills/lit-typographic-motion/engine/core/presets.mjs +1 -1
- package/plugin/skills/lit-typographic-motion/engine/node/assets.mjs +218 -0
- package/plugin/skills/lit-typographic-motion/engine/node/blender.mjs +144 -0
- package/plugin/skills/lit-typographic-motion/engine/node/board.mjs +136 -0
- package/plugin/skills/lit-typographic-motion/engine/node/done.mjs +42 -3
- package/plugin/skills/lit-typographic-motion/engine/node/gallery.mjs +120 -0
- package/plugin/skills/lit-typographic-motion/engine/node/house.mjs +44 -0
- package/plugin/skills/lit-typographic-motion/engine/node/pipeline.mjs +18 -7
- package/plugin/skills/lit-typographic-motion/engine/node/runtime.mjs +6 -3
- package/plugin/skills/lit-typographic-motion/engine/node/scaffold.mjs +210 -0
- package/plugin/skills/lit-typographic-motion/engine/node/score.mjs +639 -0
- package/plugin/skills/lit-typographic-motion/engine/node/sound-cli.mjs +21 -4
- package/plugin/skills/lit-typographic-motion/engine/node/sound.mjs +126 -27
- package/plugin/skills/lit-typographic-motion/engine/node/stage-serve.mjs +29 -5
- package/plugin/skills/lit-typographic-motion/engine/node/stage.mjs +82 -14
- package/plugin/skills/lit-typographic-motion/engine/node/styles.mjs +214 -0
- package/plugin/skills/lit-typographic-motion/engine/node/treatment.mjs +311 -10
- package/plugin/skills/lit-typographic-motion/engine/vendor/three/LICENSE +21 -0
- package/plugin/skills/lit-typographic-motion/engine/vendor/three/addons/environments/RoomEnvironment.js +144 -0
- package/plugin/skills/lit-typographic-motion/engine/vendor/three/addons/geometries/RoundedBoxGeometry.js +155 -0
- package/plugin/skills/lit-typographic-motion/engine/vendor/three/three.module.js +54571 -0
- package/plugin/skills/lit-typographic-motion/recipes/hero-3d/expected.json +69 -0
- package/plugin/skills/lit-typographic-motion/recipes/hero-3d/index.html +139 -0
- package/plugin/skills/lit-typographic-motion/references/depth.md +122 -0
- package/plugin/skills/lit-typographic-motion/references/sound.md +74 -16
- package/plugin/skills/lit-typographic-motion/references/stage.md +29 -5
- package/plugin/skills/lit-typographic-motion/references/studio.md +245 -0
- package/plugin/skills/lit-typographic-motion/references/treatment.md +31 -8
- package/plugin/skills/lit-typographic-motion/scripts/motion.mjs +48 -7
- package/plugin/skills/lit-typographic-motion/styles/paper.json +41 -0
- package/plugin/skills/lit-typographic-motion/styles/swiss-signal.json +41 -0
- package/plugin/skills/lit-typographic-motion/styles/terminalcore.json +42 -0
- package/plugin/skills/lit-typographic-motion/styles/tidal.json +41 -0
- package/plugin/skills/readme-studio/SKILL.md +1 -1
- package/plugin/skills/start-work/SKILL.md +6 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "litmuse.motion-cover-recipe/v1",
|
|
3
|
+
"note": "The decoded gate row this recipe reached when it shipped (scripts/qa-cover-recipes.mjs re-renders it and fails when median d or moving leave +-15 %, seamLocal leaves TARGET or bytes pass 2 MiB). Measured 2026-10-25 on headless Chrome's GPU rung (ANGLE Metal, Apple M5 Pro) with three.js r170 and libwebp; 20 fps keeps every frame on the 50 ms gate grid.",
|
|
4
|
+
"row": {
|
|
5
|
+
"medianD": 0.002485,
|
|
6
|
+
"movingFrac": 1,
|
|
7
|
+
"longestStillSec": 0.05,
|
|
8
|
+
"spike": 3.684,
|
|
9
|
+
"seamLocal": 1.199,
|
|
10
|
+
"bytes": 1060856
|
|
11
|
+
},
|
|
12
|
+
"settings": {
|
|
13
|
+
"size": [
|
|
14
|
+
1000,
|
|
15
|
+
500
|
|
16
|
+
],
|
|
17
|
+
"fps": 20,
|
|
18
|
+
"frames": 160,
|
|
19
|
+
"encode": {
|
|
20
|
+
"q": 94,
|
|
21
|
+
"edgeQ": 97,
|
|
22
|
+
"edgeFrames": 15,
|
|
23
|
+
"method": 4
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"treatment": {
|
|
27
|
+
"mode": "cover",
|
|
28
|
+
"request": "A README motion cover for LitMuse with a product object that opens.",
|
|
29
|
+
"idea": "A case turns once and opens on its beat to show the lit core inside, beside a still name.",
|
|
30
|
+
"recipe": "hero-3d",
|
|
31
|
+
"recipeReason": "One object acting once per loop says what the project holds better than a type pattern.",
|
|
32
|
+
"hero": "A rounded case with a hinged lid and a lit core.",
|
|
33
|
+
"beat": "The lid opens, the core rises and settles, and the lid closes.",
|
|
34
|
+
"brandWord": "LITMUSE",
|
|
35
|
+
"motto": {
|
|
36
|
+
"text": "KEEP THE WORK LIT.",
|
|
37
|
+
"source": "project",
|
|
38
|
+
"from": "README.md"
|
|
39
|
+
},
|
|
40
|
+
"aspect": "2:1",
|
|
41
|
+
"loopSec": 8,
|
|
42
|
+
"fps": 20,
|
|
43
|
+
"layers": {
|
|
44
|
+
"slow": "The case turns exactly once per loop.",
|
|
45
|
+
"mid": "The camera breathes twice per loop.",
|
|
46
|
+
"beat": "The lid opens on the core and a ring runs out across the floor."
|
|
47
|
+
},
|
|
48
|
+
"system": {
|
|
49
|
+
"rule": "One hinge rule and one turn rule on one object.",
|
|
50
|
+
"copies": "One case, one core, one ring.",
|
|
51
|
+
"offset": "The ring trails the lid by a tenth of the loop."
|
|
52
|
+
},
|
|
53
|
+
"title": {
|
|
54
|
+
"box": [
|
|
55
|
+
0.05,
|
|
56
|
+
0.36,
|
|
57
|
+
0.43,
|
|
58
|
+
0.14
|
|
59
|
+
],
|
|
60
|
+
"kind": "live"
|
|
61
|
+
},
|
|
62
|
+
"encode": {
|
|
63
|
+
"q": 94,
|
|
64
|
+
"edgeQ": 97,
|
|
65
|
+
"edgeFrames": 15
|
|
66
|
+
},
|
|
67
|
+
"ambition": "The object reads as a product in a studio and its beat lands once per loop."
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<!-- Recipe D, hero 3D: one product object, lit in a small studio, acting its beat once per loop. A
|
|
6
|
+
worked example: a rounded case turns exactly once per loop (slow layer), the camera breathes twice
|
|
7
|
+
(mid layer), and once per loop the lid opens, a lit core rises and settles back and the lid closes,
|
|
8
|
+
while a ring runs out across the floor (the beat). The word sits still on the left. Drawn with the
|
|
9
|
+
pinned three.js build on the GPU rung (software when the GPU is missing). Replace the word, the
|
|
10
|
+
motto and the HERO block with the project's own object. -->
|
|
11
|
+
<link rel="stylesheet" href="/lit/fonts.css">
|
|
12
|
+
<script src="/lit/loop-kit.js"></script>
|
|
13
|
+
<script type="importmap">{ "imports": { "three": "/lit/three/three.module.js", "three/addons/": "/lit/three/addons/" } }</script>
|
|
14
|
+
<style>
|
|
15
|
+
html, body { margin: 0; width: 1200px; height: 600px; overflow: hidden; background: #0b0f16; }
|
|
16
|
+
canvas { position: absolute; left: 0; top: 0; width: 1200px; height: 600px; display: block; }
|
|
17
|
+
</style>
|
|
18
|
+
</head>
|
|
19
|
+
<body>
|
|
20
|
+
<canvas id="world"></canvas>
|
|
21
|
+
<canvas id="type" width="2400" height="1200"></canvas>
|
|
22
|
+
<script type="module">
|
|
23
|
+
import * as THREE from "three";
|
|
24
|
+
import { RoomEnvironment } from "three/addons/environments/RoomEnvironment.js";
|
|
25
|
+
import { RoundedBoxGeometry } from "three/addons/geometries/RoundedBoxGeometry.js";
|
|
26
|
+
|
|
27
|
+
const { TAU, clamp, fract, ease } = LitLoop;
|
|
28
|
+
const span = (a, b, t) => clamp((t - a) / (b - a));
|
|
29
|
+
const C = { bg: "#0b0f16", ivory: "#f2efdf", dim: "#8b8f8f", orange: "#ff6337", shell: "#d9d4c4", inner: "#1a2230" };
|
|
30
|
+
const W = 1200, H = 600;
|
|
31
|
+
const AIM = { x: 860, y: 318 };
|
|
32
|
+
|
|
33
|
+
// ---- the world: renderer, studio light, floor ----
|
|
34
|
+
const glCanvas = document.getElementById("world");
|
|
35
|
+
const renderer = new THREE.WebGLRenderer({ canvas: glCanvas, antialias: true, alpha: true, preserveDrawingBuffer: true });
|
|
36
|
+
renderer.setPixelRatio(window.devicePixelRatio);
|
|
37
|
+
renderer.setSize(W, H, false);
|
|
38
|
+
renderer.setClearColor(0x000000, 0);
|
|
39
|
+
renderer.outputColorSpace = THREE.SRGBColorSpace;
|
|
40
|
+
renderer.toneMapping = THREE.ACESFilmicToneMapping;
|
|
41
|
+
renderer.shadowMap.enabled = true;
|
|
42
|
+
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
|
|
43
|
+
const scene = new THREE.Scene();
|
|
44
|
+
scene.environment = new THREE.PMREMGenerator(renderer).fromScene(new RoomEnvironment(), 0.04).texture;
|
|
45
|
+
scene.environmentIntensity = 0.55;
|
|
46
|
+
const cam = new THREE.PerspectiveCamera(30, W / H, 0.1, 60);
|
|
47
|
+
cam.setViewOffset(W, H, W / 2 - AIM.x, H / 2 - AIM.y, W, H);
|
|
48
|
+
const key = new THREE.DirectionalLight(0xffffff, 2.4);
|
|
49
|
+
key.position.set(-3, 6, 4);
|
|
50
|
+
key.castShadow = true;
|
|
51
|
+
key.shadow.mapSize.set(2048, 2048);
|
|
52
|
+
key.shadow.radius = 5;
|
|
53
|
+
key.shadow.bias = -0.0004;
|
|
54
|
+
key.shadow.normalBias = 0.03;
|
|
55
|
+
key.shadow.camera.left = -4; key.shadow.camera.right = 4; key.shadow.camera.top = 4; key.shadow.camera.bottom = -4;
|
|
56
|
+
const rim = new THREE.DirectionalLight(0xffb08f, 1.1);
|
|
57
|
+
rim.position.set(4, 2.5, -5);
|
|
58
|
+
scene.add(key, rim);
|
|
59
|
+
const floor = new THREE.Mesh(new THREE.PlaneGeometry(30, 30), new THREE.ShadowMaterial({ opacity: 0.35 }));
|
|
60
|
+
floor.rotation.x = -Math.PI / 2;
|
|
61
|
+
floor.receiveShadow = true;
|
|
62
|
+
scene.add(floor);
|
|
63
|
+
const ring = new THREE.Mesh(new THREE.RingGeometry(0.98, 1, 128), new THREE.MeshBasicMaterial({ color: C.orange, transparent: true, opacity: 0 }));
|
|
64
|
+
ring.rotation.x = -Math.PI / 2;
|
|
65
|
+
ring.position.y = 0.002;
|
|
66
|
+
scene.add(ring);
|
|
67
|
+
|
|
68
|
+
// ---- HERO: the product object (a case with a hinged lid and a lit core) ----
|
|
69
|
+
const shell = new THREE.MeshPhysicalMaterial({ color: C.shell, roughness: 0.32, metalness: 0.05, clearcoat: 0.6, clearcoatRoughness: 0.2 });
|
|
70
|
+
const inner = new THREE.MeshStandardMaterial({ color: C.inner, roughness: 0.8 });
|
|
71
|
+
const HERO = new THREE.Group();
|
|
72
|
+
const base = new THREE.Mesh(new RoundedBoxGeometry(1.6, 0.62, 1.1, 6, 0.12), shell);
|
|
73
|
+
base.position.y = 0.31;
|
|
74
|
+
const well = new THREE.Mesh(new RoundedBoxGeometry(1.38, 0.5, 0.88, 4, 0.08), inner);
|
|
75
|
+
well.position.y = 0.4;
|
|
76
|
+
const hinge = new THREE.Group();
|
|
77
|
+
hinge.position.set(0, 0.64, -0.55);
|
|
78
|
+
const lid = new THREE.Mesh(new RoundedBoxGeometry(1.6, 0.16, 1.1, 6, 0.07), shell);
|
|
79
|
+
lid.position.set(0, 0.08, 0.55);
|
|
80
|
+
hinge.add(lid);
|
|
81
|
+
const coreMat = new THREE.MeshStandardMaterial({ color: C.orange, emissive: C.orange, emissiveIntensity: 0, roughness: 0.35 });
|
|
82
|
+
const core = new THREE.Mesh(new THREE.IcosahedronGeometry(0.24, 3), coreMat);
|
|
83
|
+
const glow = new THREE.PointLight(C.orange, 0, 4, 2);
|
|
84
|
+
core.add(glow);
|
|
85
|
+
for (const m of [base, lid, core]) m.castShadow = true;
|
|
86
|
+
base.receiveShadow = true;
|
|
87
|
+
HERO.add(base, well, hinge, core);
|
|
88
|
+
scene.add(HERO);
|
|
89
|
+
|
|
90
|
+
// The beat: open 0.28-0.44, hold, close 0.62-0.78; zero at both ends, so the loop closes.
|
|
91
|
+
const openAt = (t) => ease.smooth(span(0.28, 0.44, t)) * (1 - ease.smooth(span(0.62, 0.78, t)));
|
|
92
|
+
|
|
93
|
+
function drawWorld(t) {
|
|
94
|
+
HERO.rotation.y = -0.5 + TAU * t;
|
|
95
|
+
const open = openAt(t);
|
|
96
|
+
hinge.rotation.x = -1.45 * open;
|
|
97
|
+
const rise = ease.outBack(span(0.36, 0.5, t)) * (1 - ease.smooth(span(0.58, 0.7, t)));
|
|
98
|
+
core.position.y = 0.42 + 0.6 * rise;
|
|
99
|
+
core.rotation.y = TAU * 2 * t;
|
|
100
|
+
coreMat.emissiveIntensity = 1.1 * open;
|
|
101
|
+
glow.intensity = 3 * open;
|
|
102
|
+
const wave = ease.outCubic(span(0.38, 0.72, t));
|
|
103
|
+
ring.scale.setScalar(0.9 + 1.3 * wave);
|
|
104
|
+
ring.material.opacity = 0.55 * open * (1 - wave);
|
|
105
|
+
const breathe = Math.sin(TAU * 2 * t);
|
|
106
|
+
cam.position.set(5.8 * Math.sin(0.35), 2.3 + 0.12 * breathe, 5.8 * Math.cos(0.35));
|
|
107
|
+
cam.lookAt(0, 0.62 + 0.04 * breathe, 0);
|
|
108
|
+
renderer.render(scene, cam);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ---- the word, set once ----
|
|
112
|
+
const ctx = document.getElementById("type").getContext("2d");
|
|
113
|
+
ctx.scale(2, 2);
|
|
114
|
+
function drawType(mask) {
|
|
115
|
+
ctx.clearRect(0, 0, W, H);
|
|
116
|
+
if (mask) { ctx.fillStyle = "#000"; ctx.fillRect(0, 0, W, H); }
|
|
117
|
+
ctx.textBaseline = "alphabetic";
|
|
118
|
+
ctx.fillStyle = mask ? "#fff" : C.ivory;
|
|
119
|
+
ctx.font = '800 84px "JetBrains Mono"';
|
|
120
|
+
ctx.fillText("LITMUSE", 70, 292);
|
|
121
|
+
if (mask) return;
|
|
122
|
+
ctx.font = '500 18px "JetBrains Mono"';
|
|
123
|
+
ctx.fillStyle = C.dim;
|
|
124
|
+
let mx = 74;
|
|
125
|
+
for (const ch of "KEEP THE WORK ") { ctx.fillText(ch, mx, 338); mx += 19.6; }
|
|
126
|
+
ctx.fillStyle = C.orange;
|
|
127
|
+
ctx.font = '700 18px "JetBrains Mono"';
|
|
128
|
+
for (const ch of "LIT.") { ctx.fillText(ch, mx, 338); mx += 19.6; }
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
window.renderFrame = (t, o = {}) => {
|
|
132
|
+
glCanvas.style.visibility = o.mask ? "hidden" : "visible";
|
|
133
|
+
if (!o.mask) drawWorld(fract(t));
|
|
134
|
+
drawType(Boolean(o.mask));
|
|
135
|
+
};
|
|
136
|
+
document.fonts.load('800 84px "JetBrains Mono"').then(() => document.fonts.load('500 18px "JetBrains Mono"')).then(() => { window.renderFrame(0); window.ready = true; });
|
|
137
|
+
</script>
|
|
138
|
+
</body>
|
|
139
|
+
</html>
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Depth: the 3D route
|
|
2
|
+
|
|
3
|
+
Read this when the treatment's subject is a `depth3d` device (`depth: "3d"`), or when a cover takes
|
|
4
|
+
the `hero-3d` recipe. A drawn 3D object is the right subject when the film is about a thing that has
|
|
5
|
+
a back and an inside: a product that turns, opens, unfolds or is assembled. A flat diagram, a chart or
|
|
6
|
+
type does not need it; keep those on the 2D page.
|
|
7
|
+
|
|
8
|
+
## What the renderer gives you
|
|
9
|
+
|
|
10
|
+
- **A pinned three.js.** The skill ships three.js r170 (npm `three@0.170.0`, MIT) in
|
|
11
|
+
`engine/vendor/three/`: the unminified `three.module.js` plus two addons,
|
|
12
|
+
`environments/RoomEnvironment.js` and `geometries/RoundedBoxGeometry.js`. The renderer serves them at
|
|
13
|
+
`/lit/three/`; a page imports them through an import map:
|
|
14
|
+
|
|
15
|
+
```html
|
|
16
|
+
<script type="importmap">{ "imports": { "three": "/lit/three/three.module.js", "three/addons/": "/lit/three/addons/" } }</script>
|
|
17
|
+
<script type="module">
|
|
18
|
+
import * as THREE from "three";
|
|
19
|
+
import { RoomEnvironment } from "three/addons/environments/RoomEnvironment.js";
|
|
20
|
+
</script>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Any other path under `/lit/three/` is refused as a missing file, and the network rules still hold:
|
|
24
|
+
never load three.js, a model or a texture from a CDN. The file hashes and the licence are in
|
|
25
|
+
`engine/THIRD_PARTY_NOTICES`.
|
|
26
|
+
- **The GPU rung.** A `depth: 3d` film and a `hero-3d` cover start Chrome on the platform's GPU rung
|
|
27
|
+
(ANGLE on Metal on macOS, GL on Linux, D3D11 on Windows), with GPU rasterization of the page
|
|
28
|
+
itself still off. The renderer records the WebGL renderer string, and the gate's WebGL line names
|
|
29
|
+
the rung and the renderer. The same determinism replay runs as on the software rung: a fresh
|
|
30
|
+
browser on the same rung must reproduce the sampled frames pixel for pixel, or the render stops with
|
|
31
|
+
exit 18. On an Apple M-series Mac the 3D test page and the `hero-3d` cover replayed identically.
|
|
32
|
+
- **A labelled fallback.** When the GPU rung cannot start, or starts without WebGL, the render
|
|
33
|
+
reopens on the software rung (SwiftShader), adds a warning to the report and the manifest naming
|
|
34
|
+
the reason, and records `rung: "software"`. The film still renders, more slowly. Say in the reply
|
|
35
|
+
that it rendered without the GPU. Only when neither rung has WebGL is it exit 11.
|
|
36
|
+
|
|
37
|
+
The rung is chosen from the treatment (`depth`) or the cover recipe, never by the page.
|
|
38
|
+
|
|
39
|
+
## The scaffold's world
|
|
40
|
+
|
|
41
|
+
`motion.mjs scaffold` on a `depth: 3d` treatment adds, on top of the 2D shots:
|
|
42
|
+
|
|
43
|
+
- `<canvas id="world">` behind every shot's section, so the type stays DOM text the QA can read;
|
|
44
|
+
- one renderer for the whole film (alpha, `preserveDrawingBuffer`, pixel ratio 1, ACES tone mapping,
|
|
45
|
+
soft shadows), a `RoomEnvironment` for reflections, a key light with a biased shadow, a hemisphere
|
|
46
|
+
light in the style's colours and a shadow-only floor;
|
|
47
|
+
- `HERO`, a group that holds the protagonist from the first frame to the last;
|
|
48
|
+
- `aim(layout)`, which offsets the camera so the world origin lands on the centre of the shot's subject
|
|
49
|
+
zone; a `hero` layout shot therefore fills the frame and a `split` shot keeps the object on its side;
|
|
50
|
+
- `WORLD[i](s, hero, cam, t)`, one function per shot that poses the hero, the camera and anything else
|
|
51
|
+
as a function of the shot (`s.p`, `s.local`, `s.enter(...)`).
|
|
52
|
+
|
|
53
|
+
Build the protagonist once, in `HERO`, and only pose it per shot: that is what makes it the same
|
|
54
|
+
object across the film. For a named product, start from its real logo and screens: a fetched logo or
|
|
55
|
+
screenshot goes on a plane or a face of the object as a texture (`TextureLoader` on the file under
|
|
56
|
+
`stage/assets/`); a simple logo shape can be traced into a `THREE.Shape` and given depth with
|
|
57
|
+
`ExtrudeGeometry`.
|
|
58
|
+
|
|
59
|
+
## Craft and the gate
|
|
60
|
+
|
|
61
|
+
- **Turn slowly.** A full turn takes the loop (a cover) or a whole shot (a film). Fast spins, a flicker
|
|
62
|
+
of specular highlights and shadow acne all read as flashes and fail MO-C-03.
|
|
63
|
+
- **Bias the shadow.** On a turning object set `shadow.bias` about -0.0004 and `shadow.normalBias`
|
|
64
|
+
about 0.03; without them the self-shadow flickers frame to frame.
|
|
65
|
+
- **Ease every open and close.** A lid, a drawer or an exploded view eases in and out (`ease.smooth`),
|
|
66
|
+
holds, and returns; the move is the beat, so it happens once, where the treatment's story puts it.
|
|
67
|
+
- **Keep the type in the DOM.** Copy stays in the shot's type zone as HTML text over the canvas;
|
|
68
|
+
text drawn inside the 3D scene is invisible to the QA unless registered with `LitStage.text`.
|
|
69
|
+
- **Mind the dead air.** A small object on a large empty ground is `DEAD-AIR`; frame it with the
|
|
70
|
+
`hero` layout or move the camera in.
|
|
71
|
+
- **Covers at 20 fps.** The cover gate measures on a 50 ms grid; at 25 fps a smooth turn lands two
|
|
72
|
+
frames on one step at the seam (FLOOR), at 20 fps every step is one frame (TARGET). Encode at q94:
|
|
73
|
+
smooth light surfaces blotch at the default q86.
|
|
74
|
+
|
|
75
|
+
## Camera moves and the flash audit
|
|
76
|
+
|
|
77
|
+
A rich film moves its camera in at least half its shots, and the flash audit stays exactly as it is.
|
|
78
|
+
When a move fails MO-C-03, keep the move and change what it passes over: slow the light change (a
|
|
79
|
+
highlight that sweeps over 0.5 s or more, not a frame), lower the contrast of the detail behind the
|
|
80
|
+
move, add motion blur (the Blender rung renders it; on the GPU rung average 4-8 sub-frame poses), or cut
|
|
81
|
+
instead of whipping. Removing the camera move is a downgrade.
|
|
82
|
+
|
|
83
|
+
## The Blender rung (`render3d: "blender"`)
|
|
84
|
+
|
|
85
|
+
For path-traced light (bevels that catch it, soft global illumination, depth of field, motion blur) a
|
|
86
|
+
3D film can render its world in Blender with Cycles. The page then shows the rendered plate and keeps
|
|
87
|
+
its type, labels and 2D layers in the DOM over it.
|
|
88
|
+
|
|
89
|
+
Take this rung when the request asks for high-quality, cinematic or photoreal 3D (고퀄리티, 고품질,
|
|
90
|
+
시네마틱, 실사, cinematic, photoreal, high-quality, Blender) and Blender is installed: `scaffold` prints
|
|
91
|
+
`BLENDER:` with the path when both hold. On an Apple M-series GPU a full hero scene rendered at 8-10 s
|
|
92
|
+
per 1080p frame at 160 samples, so a 13 s plate (390 frames) takes about an hour. Settle the scene on
|
|
93
|
+
the preview stills first, since a fix after the final means rendering the plate again, and build the
|
|
94
|
+
page's panels while it renders.
|
|
95
|
+
|
|
96
|
+
1. Set `depth: 3d`, a `depth3d` subject and `render3d: "blender"`.
|
|
97
|
+
2. Write `stage/scene.py`, a Blender Python scene. It runs inside the bundled runner with `bpy`,
|
|
98
|
+
`math`, `Vector` and `lit` in scope; times are film seconds and the plate starts at frame 1:
|
|
99
|
+
`lit.camera(location, target, lens, dof=(distance, fstop))`, `lit.area_light(name, location, target,
|
|
100
|
+
power, size, color)`, `lit.world(color, strength)`, `lit.material(name, color, roughness, metallic,
|
|
101
|
+
emission, strength)`, `lit.rounded_box(name, size, radius, segments, material, location)`,
|
|
102
|
+
`lit.image_card(name, path, width, location)` (a fetched product image as a card) and
|
|
103
|
+
`lit.key(obj, prop, t, value)`. The scene must set a camera. The command line sets size, samples,
|
|
104
|
+
device, fps and frames; the scene sets everything else.
|
|
105
|
+
3. `motion.mjs blender --out <dir>` renders preview stills at the beat midpoints (50 %, 40 samples) into
|
|
106
|
+
`stage/plate-preview/`. Open them, compare them with any reference frames the user gave, and fix
|
|
107
|
+
`scene.py` before the final.
|
|
108
|
+
4. `motion.mjs blender --out <dir> --final` renders the plate (full size, 160 samples, motion blur,
|
|
109
|
+
30 fps, `stage/plate/000001.png ...`) detached with a progress file; it can take most of an hour.
|
|
110
|
+
It writes `plate.json`, which lists every frame by name and size with the Blender version, device and
|
|
111
|
+
samples. Listed plate frames are not counted as page rasters; any other frame sequence is still a
|
|
112
|
+
flipbook (exit 17).
|
|
113
|
+
5. On the page, `const show = LitStage.plate({ parent, count, fps: 30 })` returns `show(t)`; call it from
|
|
114
|
+
`render(t)`. The page renders on the software rung, and the stage refuses to start (exit 17) when the
|
|
115
|
+
plate is missing or does not cover the film.
|
|
116
|
+
|
|
117
|
+
Blender is found from `LITMUSE_BLENDER` (used only if it exists), the macOS app
|
|
118
|
+
(`/Applications/Blender.app/Contents/MacOS/Blender`) or `blender` on PATH, and runs as `blender -b
|
|
119
|
+
--factory-startup -P runner.py -- ...`, on Metal on Apple silicon, another GPU backend next, the CPU
|
|
120
|
+
last; the receipt says which. Without Blender the command renders nothing and prints `FALLBACK`: set
|
|
121
|
+
`render3d` to `three`, build the world in three.js on the GPU rung, and say so in the reply. The report
|
|
122
|
+
and manifest name the plate (Blender version, Cycles device, samples).
|
|
@@ -1,28 +1,67 @@
|
|
|
1
1
|
# Sound
|
|
2
2
|
|
|
3
|
-
Films have sound unless the user said otherwise. The treatment's `sound` object (`{ mode, plan,
|
|
4
|
-
file }`) plans it on both paths, the renderer muxes the track the plan names, and after
|
|
5
|
-
decodes the film's audio stream and gates it. The generated bed
|
|
6
|
-
the treatment: no sample library, model or package is downloaded for
|
|
7
|
-
produces the same bytes.
|
|
3
|
+
Films have sound unless the user said otherwise. The treatment's `sound` object (`{ mode, plan, genre?,
|
|
4
|
+
palette?, events?, file? }`) plans it on both paths, the renderer muxes the track the plan names, and after
|
|
5
|
+
the encode it decodes the film's audio stream and gates it. The score and the generated bed are synthesized
|
|
6
|
+
by LitMuse's own code from the treatment: no sample library, model or package is downloaded for either, no
|
|
7
|
+
melody is borrowed, and the same treatment always produces the same bytes.
|
|
8
8
|
|
|
9
9
|
## Choosing a mode
|
|
10
10
|
|
|
11
11
|
| `sound.mode` | Track | Use it when |
|
|
12
12
|
| --- | --- | --- |
|
|
13
|
-
| `
|
|
13
|
+
| `score` | A layered score built for this film: drums, bass, chords, lead, arrangement FX and the on-screen events as separate stems, mixed and mastered | The default under a bare `lit` and for every rich film, on either path |
|
|
14
|
+
| `generated` | One of the style's preset beds: a pad chord progression with a pulse and the accents the beats ask for | Minimal films only (`register: minimal`) |
|
|
14
15
|
| `supplied` | The user's file at `sound.file` (relative to the output folder), padded or trimmed to the film | The user handed over a track |
|
|
15
16
|
| `authored` | A WAV you wrote into the stage folder, named by `sound.file` | The plan needs sound the bed cannot make; the file must sit inside `<dir>/stage/` |
|
|
16
17
|
| `none` | No audio stream | The request asks for silence (무음, 소리 없이, silent, muted, no music …) or the `channel` plays muted by design |
|
|
17
18
|
|
|
18
19
|
A supplied or authored track is always muxed, whatever the state of the optional analysis tier below.
|
|
19
|
-
The reply calls
|
|
20
|
-
file name. Switching to `none` to get past a
|
|
21
|
-
reply has to admit it.
|
|
22
|
-
|
|
23
|
-
## The
|
|
20
|
+
The reply calls the track "an original score generated for this film" (or "a generated music bed" for a
|
|
21
|
+
minimal film) in plain words and names a supplied track by its file name. Switching to `none` to get past a
|
|
22
|
+
failing check is a downgrade: the done-check records it and the reply has to admit it.
|
|
23
|
+
|
|
24
|
+
## The score
|
|
25
|
+
|
|
26
|
+
The score is produced like a house track from the film itself, so no two films share one.
|
|
27
|
+
|
|
28
|
+
- **Genre.** `house` (120–126 BPM, four-on-the-floor), `tech-house` (124–128, rolling bass), `deep-house`
|
|
29
|
+
(118–122, pads, light swing) or `garage` (130–134, broken kick, heavy swing). The style picks one
|
|
30
|
+
(swiss-signal house, terminalcore tech-house, tidal deep-house, paper garage); `sound.genre` names
|
|
31
|
+
another when the film asks for it. Launch and hero films stay in house or tech-house.
|
|
32
|
+
- **Key, tempo, progression.** Seeded from the request, idea, turn, protagonist and beats: one of twelve
|
|
33
|
+
keys, major or minor, a tempo inside the genre's band, one progression per mode, the last bar home.
|
|
34
|
+
- **Arrangement from the beats.** The shots before `story.beat` are intro and build; the drop lands on the
|
|
35
|
+
turn, on a downbeat; with three or more shots after the turn the first is a break and the rest drop again;
|
|
36
|
+
the last shot is the outro. Risers run into every change (strongest into the drop), an impact lands on it,
|
|
37
|
+
a downlifter follows, and a reverse swell breathes into the other changes, with the kick out for the half
|
|
38
|
+
bar before the drop and a snare roll into it.
|
|
39
|
+
- **Stems.** `kick`, `clap`, `hats`, `perc`, `bass` (sub and line), `chords` (stab and pad), `lead` (a motif
|
|
40
|
+
off the key's pentatonic), `fx` (risers, impacts, downlifters, reverse swells) and `events`, each its own
|
|
41
|
+
pattern in its own buffer.
|
|
42
|
+
- **Mix.** Per-stem gain and pan, a high-pass on everything but kick and bass, bass/chords/lead ducked under
|
|
43
|
+
the kick (the sidechain depth is measured and reported in dB), reverb and tempo-delay sends, bus
|
|
44
|
+
compression, then the shared master stage (−16 LUFS, peak under −2 dBFS).
|
|
45
|
+
|
|
46
|
+
Write `sound.plan` as the producer's brief (where the drop lands, where it breaks) and put the cue words
|
|
47
|
+
in the beats' `sound` fields. `motion.mjs sound` writes `sound/score.wav`, every stem and both returns under
|
|
48
|
+
`sound/stems/`, and `sound-cues.json` with genre, key, tempo, progression, sections, FX, stem levels,
|
|
49
|
+
sidechain depth, bus settings and every cue.
|
|
50
|
+
|
|
51
|
+
## Palettes (minimal films)
|
|
52
|
+
|
|
53
|
+
A minimal film's generated bed takes one of its style's beds exactly (the validator refuses anything
|
|
54
|
+
else); the first is the default and the other two let two films in one style differ:
|
|
55
|
+
|
|
56
|
+
| Style | Default bed | Alternatives |
|
|
57
|
+
| --- | --- | --- |
|
|
58
|
+
| `swiss-signal` | glass, A minor, 104 | pulse-synth, D minor, 112; soft-keys, C major, 96 |
|
|
59
|
+
| `tidal` | felt-strings, D major, 84 | glass, F# minor, 76; soft-keys, Bb major, 72 |
|
|
60
|
+
| `terminalcore` | pulse-synth, E minor, 120 | glass, B minor, 132; pulse-synth, A minor, 100 |
|
|
61
|
+
| `paper` | soft-keys, F major, 96 | felt-strings, G major, 88; glass, C major, 108 |
|
|
24
62
|
|
|
25
|
-
|
|
63
|
+
Pick the bed for the film's mood and pace: a slower or tenderer film the slower bed, a launch the
|
|
64
|
+
brighter one. `sound.palette` is:
|
|
26
65
|
|
|
27
66
|
```json
|
|
28
67
|
{ "timbre": "<timbre>", "key": "<pitch class>", "mode": "<major or minor>", "tempo": "<60-160>" }
|
|
@@ -61,6 +100,24 @@ The bed adds an accent only where a beat's `sound` text asks for one. It reads t
|
|
|
61
100
|
A beat that names none of these gets pad and pulse only. The cut times come from the beats on the stage
|
|
62
101
|
path and from the brief's shots on the type path.
|
|
63
102
|
|
|
103
|
+
## Events
|
|
104
|
+
|
|
105
|
+
Cue words tie accents to cuts. Events tie sounds to what moves inside a shot: a latch clicks when the lid
|
|
106
|
+
opens, a card lands, a cursor types, a progress bar completes. A score or generated bed on the stage path
|
|
107
|
+
lists them in `sound.events`:
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
"events": [{ "t": 6.42, "on": "the lid clicks open", "kind": "tap" }]
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
- `t`: the second the event happens on screen, read from the page's own timing, never guessed from the beat;
|
|
114
|
+
- `on`: what happens at that frame, as a viewer would see it;
|
|
115
|
+
- `kind`: `tap` (a short click with a pitched body), `hit` (the cut hit), `whoosh` (air ending on the event)
|
|
116
|
+
or `chime` (two notes of the key: something completes).
|
|
117
|
+
|
|
118
|
+
At least two events, and at least half of them 0.1 s or more away from every cut, so the sound follows the
|
|
119
|
+
picture rather than only the edit. An event never snaps to a cut: it sounds at its own `t`.
|
|
120
|
+
|
|
64
121
|
`<dir>/sound-cues.json` records each cue with `kind`, `t` in seconds, `beatIndex`, `target` (the boundary or
|
|
65
122
|
cut it serves) and `delta` (`t - target`), alongside the timbre, key, tempo, loudness, peak, sample count
|
|
66
123
|
and the bed's SHA-256. Look question 6 ("does the sound follow the cuts?") is answered from this file: every
|
|
@@ -102,10 +159,11 @@ the field named.
|
|
|
102
159
|
node "$SKILL_ROOT/scripts/motion.mjs" sound --out <dir>
|
|
103
160
|
```
|
|
104
161
|
|
|
105
|
-
This reads `<dir>/treatment.json` and writes `<dir>/sound/
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
162
|
+
This reads `<dir>/treatment.json` and writes the score (`<dir>/sound/score.wav` with its stems) or a
|
|
163
|
+
minimal film's bed (`<dir>/sound/bed.wav`), plus `<dir>/sound-cues.json`, so the track can be heard and its
|
|
164
|
+
cues checked before a render. For a supplied, authored or silent plan it only reports what the render will
|
|
165
|
+
do. The full render rebuilds the track at the film's exact frame count and cut times, so the muxed track
|
|
166
|
+
always matches the film sample for sample. Two builds from the same treatment give the same bytes.
|
|
109
167
|
|
|
110
168
|
## The optional analysis tier
|
|
111
169
|
|
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
Load this once the treatment says `path: stage`. On this path the film is a web page you write, and the
|
|
4
4
|
LitMuse renderer turns it into frames: it serves the page to a headless Chrome on a private origin,
|
|
5
5
|
drives a virtual clock from zero, captures every frame in order, then encodes, replays and gates the
|
|
6
|
-
result. You draw everything the treatment promised, the subject first. The
|
|
6
|
+
result. You draw everything the treatment promised, the subject first. The house style gives the frame
|
|
7
|
+
(palette, type roles, layout zones, motion tokens, hand-offs), served as `/lit/style.css` and
|
|
8
|
+
`/lit/style.js`. Start every page from `motion.mjs scaffold --out <dir>`, which writes the treatment's
|
|
9
|
+
structure for you; fill its `DRAW` functions with the drawing. The kit described below supplies
|
|
7
10
|
timing and reveal primitives and nothing pictorial, so no two stage films start from the same template.
|
|
8
11
|
|
|
9
12
|
## The page contract
|
|
@@ -12,10 +15,14 @@ The page lives at `<dir>/stage/index.html`, next to the local files it uses (SVG
|
|
|
12
15
|
CSS, WAV). Only that folder is served, under the origin `http://lit.stage`; a symlink that resolves
|
|
13
16
|
outside it is a contract error.
|
|
14
17
|
|
|
15
|
-
|
|
18
|
+
Four routes come from the renderer itself and are always available:
|
|
16
19
|
|
|
17
20
|
- `<link rel="stylesheet" href="/lit/fonts.css">` declares the verified faces (table below).
|
|
18
21
|
- `<script src="/lit/stage-kit.js"></script>` defines the global `LitStage`.
|
|
22
|
+
- `<link rel="stylesheet" href="/lit/style.css">` and `<script src="/lit/style.js"></script>` serve the
|
|
23
|
+
film's house style for its format: custom properties, the type-role and colour classes, `window.LitStyle`
|
|
24
|
+
(tokens and pixel zones) and `window.LitTimeline` (the treatment's shots). Set copy with the classes;
|
|
25
|
+
the gate checks the rendered result either way.
|
|
19
26
|
|
|
20
27
|
The page hands the renderer its spec with `LitStage.define({ width, height, fps, duration, render })`, or
|
|
21
28
|
by assigning the same object to `window.litStage`. `define` checks the spec as soon as it is called:
|
|
@@ -193,9 +200,11 @@ node "$SKILL_ROOT/scripts/motion.mjs" stage --out <dir> --round 2
|
|
|
193
200
|
```
|
|
194
201
|
|
|
195
202
|
The stills-only round writes `<dir>/stills/`: one PNG per beat midpoint, one transition strip per cut (the
|
|
196
|
-
frames 6 before, at and 6 after the cut, side by side at half size), a 12-frame contact sheet,
|
|
197
|
-
`stills/index.json`. Nothing is encoded.
|
|
198
|
-
|
|
203
|
+
frames 6 before, at and 6 after the cut, side by side at half size), a 12-frame contact sheet,
|
|
204
|
+
`stills/board.png` with `stills/check.json`, and `stills/index.json`. Nothing is encoded. The board round
|
|
205
|
+
also runs the board check, the gate's text and style rules on the board frames, and exits 13 naming each
|
|
206
|
+
failed rule. Open the board and those PNGs with `read_file` and record the round as `references/look.md`
|
|
207
|
+
describes before the full render.
|
|
199
208
|
|
|
200
209
|
The full render captures the master, encodes the MP4 (with the planned sound muxed), builds the preview
|
|
201
210
|
WebP or GIF, the poster and the reduced-motion still, runs the determinism replay and the text QA in
|
|
@@ -203,6 +212,21 @@ parallel, and writes `gate-report.txt`, `manifest.json` and `render.jsonl`. It r
|
|
|
203
212
|
phase to `<dir>/.run/progress.json`. If the `bash` call times out, run the same command again: it attaches
|
|
204
213
|
to the render already running and returns its result. Never cut the film short to finish sooner.
|
|
205
214
|
|
|
215
|
+
## The 3D route
|
|
216
|
+
|
|
217
|
+
When the treatment says `depth: 3d`, the subject is drawn as a three.js world (`references/depth.md`).
|
|
218
|
+
The scaffold page already carries the shape: an import map onto the vendored build at `/lit/three/`, a
|
|
219
|
+
full-frame canvas behind the shots, one persistent protagonist group `HERO`, and one `WORLD` function per
|
|
220
|
+
shot that poses the hero, the camera and anything else as a function of the shot's progress. The renderer
|
|
221
|
+
serves the pinned build and its few addons; pages import them through the import map, never from a URL.
|
|
222
|
+
A 3D film asks for the GPU rung; the software rung is the fallback, slower, with the same frames.
|
|
223
|
+
|
|
224
|
+
A path-traced look with Blender installed takes the offline rung instead (`render3d: blender`): write the
|
|
225
|
+
world in `stage/scene.py`, render preview stills with `motion.mjs blender --out <dir>`, then the plate with
|
|
226
|
+
`--final` into `stage/plate/`, and show it under HTML type and labels with `LitStage.plate()`. The plate
|
|
227
|
+
frames are listed by name and size in `stage/plate/plate.json`; the scan trusts them and holds everything
|
|
228
|
+
else to the raster limits.
|
|
229
|
+
|
|
206
230
|
## The stage gate
|
|
207
231
|
|
|
208
232
|
`gate-report.txt` ends with `QA gate: PASS` or lists the failed rule ids. The stage rules are:
|