mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-12 23:29:50 +00:00
initial code (#2)
* feat: initial code port from hyperframes-internal Port all OSS-ready packages from the internal monorepo: - @hyperframes/core — shared types, HTML generation, GSAP utilities, runtime - @hyperframes/cli — CLI for creating, previewing, and rendering compositions - @hyperframes/engine — framework-agnostic rendering engine (BeginFrame + FFmpeg) - @hyperframes/producer — video rendering pipeline (Puppeteer + FFmpeg) - @hyperframes/ui-player — browser-based video player component - @hyperframes/studio — composition editor (React frontend + Hono backend) Includes regression test suite with Docker-based test harness. All HeyGen-internal references, deployment infrastructure, and proprietary assets have been removed. Package names migrated from @app/* to @hyperframes/*. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: scrub internal codenames and stale references from OSS port - Replace static.heygen.ai runtime URLs in test fixtures - Remove internal CDN publish script (publish-hyperframe-runtime.ts) - Replace sandbox-studio, sandbox-interceptor, __magicEditRuntime with neutral names (studio, hyperframe-runtime, __hyperframeRuntime) - Fix stale Vault API / localhost references in docs - Remove broken deprecated_studio link Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: remove remaining internal codenames and stale references - Delete stale producer README.md and PIPELINE.md (referenced nonexistent files) - Replace "Cerberus" codename with "HyperFrames" in test design reviews - Replace magic-edit postMessage identifiers with hf-preview/hf-parent - Rename debug-magic-edit-timeline.ts to debug-timeline.ts - Replace "Motion Cut" with "HyperFrames" in Timeline comments - Fix studio/CLI references to nonexistent archive package (use local data/projects/ dir, stub render proxy) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
10621e7903
commit
9f8e5ba5a1
@@ -0,0 +1,63 @@
|
||||
# HyperFrame Schema Compliance Review
|
||||
|
||||
## Executive Summary
|
||||
- Total files reviewed: 3
|
||||
- Critical issues: 0
|
||||
- Overall compliance status: PASS
|
||||
|
||||
## Critical Issues
|
||||
None. The compositions follow the deterministic requirements and essential timeline registration rules.
|
||||
|
||||
## Compliance Checklist
|
||||
- [x] All compositions have `data-width` and `data-height` attributes
|
||||
- [x] All timelines are finite with duration > 0
|
||||
- [x] All compositions registered in `window.__timelines`
|
||||
- [x] No use of `Math.random()`, `Date.now()`, or non-deterministic code
|
||||
- [x] Primitive clips have required data attributes (`id`, `data-start`, `data-track`)
|
||||
- [x] `data-duration` specified for all `<img>` clips (N/A - no images used)
|
||||
- [x] No manual media playback control (`video.play()`, `audio.pause()`, etc.)
|
||||
- [x] No manual clip mounting/unmounting in scripts
|
||||
- [x] Relative timing references are valid (N/A - absolute timing used)
|
||||
- [x] Clips on same track don't overlap in time
|
||||
- [x] Reusable compositions in separate HTML files
|
||||
- [x] Composition files use `<template>` tags
|
||||
- [x] External compositions loaded via `data-composition-src`
|
||||
- [x] All script-animated content wrapped in compositions
|
||||
- [x] No infinite or zero-duration timelines
|
||||
|
||||
## File Reviews
|
||||
|
||||
### index.html
|
||||
**Status**: COMPLIANT
|
||||
|
||||
**Observations**:
|
||||
- Correctly defines the root composition `main-video`.
|
||||
- Uses `data-width="1920"` and `data-height="1080"` as requested for Landscape orientation.
|
||||
- Properly registers `window.__timelines["main-video"]`.
|
||||
- Loads sub-compositions using `data-composition-src`.
|
||||
- Primitive clips (`video`, `audio`) have required `id`, `data-start`, and `data-track`.
|
||||
- Track assignments are clean: Track 0 (BG), Track 1 (Video), Track 2 (Graphics), Track 3 (Captions), Track 4 (Audio).
|
||||
|
||||
### compositions/captions.html
|
||||
**Status**: COMPLIANT
|
||||
|
||||
**Observations**:
|
||||
- Uses `<template>` tag correctly.
|
||||
- Root element has `data-composition-id`, `data-width`, `data-height`, and `data-duration`.
|
||||
- Script is deterministic, using a fixed `TRANSCRIPT` array.
|
||||
- Correctly registers `window.__timelines["captions"]`.
|
||||
- Note: The `data-composition-id` in the HTML is `captions`, and the script registers `captions`. In `index.html`, the container has `data-composition-id="captions-comp"`. The framework typically matches the registration to the ID of the element being instantiated. Since the template root has `data-composition-id="captions"`, this is the ID that should be registered.
|
||||
|
||||
### compositions/graphics.html
|
||||
**Status**: COMPLIANT
|
||||
|
||||
**Observations**:
|
||||
- Uses `<template>` tag correctly.
|
||||
- Root element has `data-composition-id`, `data-width`, `data-height`, and `data-duration`.
|
||||
- Animations are deterministic GSAP tweens.
|
||||
- Correctly registers `window.__timelines["graphics-comp"]`.
|
||||
- Layout is well-coordinated with the A-roll positioning defined in the master timeline.
|
||||
|
||||
## Recommendations
|
||||
- **Consistency**: In `index.html`, the captions container uses `data-composition-id="captions-comp"`, but the template inside `captions.html` uses `data-composition-id="captions"`. While the framework handles the swap during instantiation, keeping these IDs identical (e.g., both `captions`) improves maintainability.
|
||||
- **Deterministic Logic**: The use of `(function() { ... })()` in sub-compositions is a good practice for scoping.
|
||||
@@ -0,0 +1,94 @@
|
||||
<template id="captions-template">
|
||||
<div data-composition-id="captions" data-width="1920" data-height="1080" data-duration="17">
|
||||
<div id="captions-container"></div>
|
||||
|
||||
<style>
|
||||
[data-composition-id="captions"] {
|
||||
width: 1920px;
|
||||
height: 1080px;
|
||||
position: relative; /* Changed from flex to absolute positioning for precision */
|
||||
pointer-events: none;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
[data-composition-id="captions"] #captions-container {
|
||||
position: absolute;
|
||||
bottom: 54px; /* 5% of 1080px */
|
||||
left: 0;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
[data-composition-id="captions"] .caption-line {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
/* No left: 50% here, we rely on the container's flex centering */
|
||||
background-color: #000000; /* Dark solid background strip */
|
||||
color: #FFFFFF; /* White */
|
||||
font-family: 'Futura', 'DIN Alternate', 'Arial Black', sans-serif;
|
||||
font-weight: 900;
|
||||
font-size: 56px;
|
||||
padding: 15px 40px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
opacity: 0;
|
||||
white-space: nowrap;
|
||||
display: inline-block;
|
||||
z-index: 100;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const TRANSCRIPT = [{'text': 'We', 'start': 0.119, 'end': 0.259}, {'text': 'asked', 'start': 0.319, 'end': 0.479}, {'text': 'what', 'start': 0.519, 'end': 0.659}, {'text': 'you', 'start': 0.699, 'end': 0.819}, {'text': 'needed.', 'start': 0.859, 'end': 1.819}, {'text': 'Forty-seven', 'start': 1.86, 'end': 2.299}, {'text': 'percent', 'start': 2.399, 'end': 2.679}, {'text': 'of', 'start': 2.7, 'end': 2.799}, {'text': 'you', 'start': 2.839, 'end': 2.939}, {'text': 'said', 'start': 3.039, 'end': 3.179}, {'text': 'motion', 'start': 3.24, 'end': 3.559}, {'text': 'graphics,', 'start': 3.579, 'end': 4.599}, {'text': 'sixty-two', 'start': 4.679, 'end': 5.179}, {'text': 'percent', 'start': 5.299, 'end': 5.759}, {'text': 'said', 'start': 5.859, 'end': 5.98}, {'text': 'static', 'start': 6.079, 'end': 6.399}, {'text': 'content', 'start': 6.46, 'end': 6.879}, {'text': 'was', 'start': 6.92, 'end': 7.079}, {'text': 'costing', 'start': 7.099, 'end': 7.48}, {'text': 'you', 'start': 7.5, 'end': 7.579}, {'text': 'attention,', 'start': 7.679, 'end': 8.659}, {'text': 'and', 'start': 8.699, 'end': 8.86}, {'text': 'three', 'start': 8.88, 'end': 9.06}, {'text': 'out', 'start': 9.079, 'end': 9.18}, {'text': 'of', 'start': 9.199, 'end': 9.34}, {'text': 'four', 'start': 9.38, 'end': 9.799}, {'text': 'said', 'start': 9.84, 'end': 10.0}, {'text': 'you', 'start': 10.019, 'end': 10.159}, {'text': 'know', 'start': 10.179, 'end': 10.36}, {'text': 'the', 'start': 10.38, 'end': 10.42}, {'text': 'look', 'start': 10.519, 'end': 10.699}, {'text': 'you', 'start': 10.739, 'end': 10.859}, {'text': 'want,', 'start': 10.98, 'end': 11.34}, {'text': 'but', 'start': 11.359, 'end': 11.52}, {'text': "don't", 'start': 11.56, 'end': 11.779}, {'text': 'have', 'start': 11.819, 'end': 11.94}, {'text': 'the', 'start': 11.96, 'end': 12.06}, {'text': 'editing', 'start': 12.079, 'end': 12.4}, {'text': 'skills', 'start': 12.52, 'end': 12.859}, {'text': 'to', 'start': 12.88, 'end': 13.0}, {'text': 'get', 'start': 13.019, 'end': 13.18}, {'text': 'there.', 'start': 13.22, 'end': 14.22}, {'text': 'So', 'start': 14.239, 'end': 14.399}, {'text': 'we', 'start': 14.42, 'end': 14.52}, {'text': 'built', 'start': 14.619, 'end': 14.88}, {'text': 'Editor', 'start': 15.079, 'end': 15.42}, {'text': 'Agent.', 'start': 15.619, 'end': 16.02}];
|
||||
const container = document.getElementById('captions-container');
|
||||
const tl = gsap.timeline({ paused: true });
|
||||
|
||||
const lines = [];
|
||||
let currentLine = [];
|
||||
|
||||
TRANSCRIPT.forEach((word, index) => {
|
||||
currentLine.push(word);
|
||||
if (currentLine.length === 5 || index === TRANSCRIPT.length - 1) {
|
||||
lines.push({
|
||||
words: [...currentLine],
|
||||
text: currentLine.map(w => w.text).join(' ').toUpperCase()
|
||||
});
|
||||
currentLine = [];
|
||||
}
|
||||
});
|
||||
|
||||
lines.forEach((line, index) => {
|
||||
const startTime = line.words[0].start;
|
||||
const endTime = line.words[line.words.length - 1].end;
|
||||
|
||||
const lineEl = document.createElement('div');
|
||||
lineEl.className = 'caption-line';
|
||||
lineEl.textContent = line.text;
|
||||
container.appendChild(lineEl);
|
||||
|
||||
// Center the line element relative to the container
|
||||
gsap.set(lineEl, { left: '50%', xPercent: -50 });
|
||||
|
||||
tl.to(lineEl, {
|
||||
opacity: 1,
|
||||
y: -20,
|
||||
duration: 0.2,
|
||||
ease: 'power2.out'
|
||||
}, startTime);
|
||||
|
||||
tl.to(lineEl, {
|
||||
opacity: 0,
|
||||
y: -40,
|
||||
duration: 0.1,
|
||||
ease: 'power2.in'
|
||||
}, endTime);
|
||||
});
|
||||
|
||||
window.__timelines["captions"] = tl;
|
||||
})();
|
||||
</script>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,239 @@
|
||||
<template id="graphics-template">
|
||||
<div data-composition-id="graphics-comp" data-width="1920" data-height="1080" data-duration="17">
|
||||
<!-- Intro Elements -->
|
||||
<div class="intro-container">
|
||||
<div class="black-circle"></div>
|
||||
<div class="intro-text">ASKED & ANSWERED</div>
|
||||
</div>
|
||||
|
||||
<!-- Stat 1: 47% NEED MOTION (A-roll is at right: x:700, y:216) -->
|
||||
<div class="stat-container stat-1">
|
||||
<div class="vermillion-square"></div>
|
||||
<div class="stat-number">47%</div>
|
||||
<div class="stat-subtext">NEED MOTION</div>
|
||||
</div>
|
||||
|
||||
<!-- Stat 2: 62% INEFFECTIVE (A-roll is at left: x:68, y:216) -->
|
||||
<div class="stat-container stat-2">
|
||||
<div class="white-circle">
|
||||
<div class="stat-number">62%</div>
|
||||
<div class="stat-subtext">INEFFECTIVE</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stat 3: 75% LACK SKILLS (A-roll is centered small: x:480, y:450) -->
|
||||
<div class="stat-container stat-3">
|
||||
<div class="black-rectangle"></div>
|
||||
<div class="stat-number">75%</div>
|
||||
<div class="stat-subtext">LACK SKILLS</div>
|
||||
</div>
|
||||
|
||||
<!-- Outro: EDITOR AGENT -->
|
||||
<div class="outro-container">
|
||||
<div class="outro-circle"></div>
|
||||
<div class="outro-text">EDITOR AGENT</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
[data-composition-id="graphics-comp"] {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 1920px;
|
||||
height: 1080px;
|
||||
font-family: 'Futura', 'DIN Alternate', 'Arial Black', sans-serif;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Common Styles */
|
||||
.intro-container, .stat-container, .outro-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-size: 200px;
|
||||
font-weight: 900;
|
||||
line-height: 1;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.stat-subtext {
|
||||
position: absolute;
|
||||
font-size: 60px;
|
||||
font-weight: 900;
|
||||
z-index: 3;
|
||||
text-align: center;
|
||||
width: 800px;
|
||||
}
|
||||
|
||||
/* Intro Styles - Centered behind A-roll (A-roll is full screen) */
|
||||
.black-circle {
|
||||
width: 800px;
|
||||
height: 800px;
|
||||
background-color: #000000;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.intro-text {
|
||||
font-size: 100px;
|
||||
font-weight: 900;
|
||||
color: #D0021B;
|
||||
z-index: 2;
|
||||
text-align: center;
|
||||
width: 1000px;
|
||||
}
|
||||
|
||||
/* Stat 1 Styles (Left side) - A-roll is at x:700 */
|
||||
.stat-1 .vermillion-square {
|
||||
width: 600px;
|
||||
height: 600px;
|
||||
background-color: #D0021B;
|
||||
position: absolute;
|
||||
left: 50px;
|
||||
top: 240px;
|
||||
}
|
||||
|
||||
.stat-1 .stat-number {
|
||||
color: #FFFFFF;
|
||||
position: absolute;
|
||||
left: 100px;
|
||||
top: 400px;
|
||||
}
|
||||
|
||||
.stat-1 .stat-subtext {
|
||||
color: #000000;
|
||||
left: 100px;
|
||||
top: 650px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* Stat 2 Styles (Right side) - A-roll is at x:68 */
|
||||
.stat-2 {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.stat-2 .white-circle {
|
||||
width: 550px; /* Scaled down from 650px to avoid clipping */
|
||||
height: 550px;
|
||||
background-color: #D0021B; /* Vermillion background */
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
right: 100px; /* Moved further right to avoid A-roll */
|
||||
top: 265px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.stat-2 .stat-number {
|
||||
color: #000000; /* Black text */
|
||||
position: relative;
|
||||
right: auto;
|
||||
top: auto;
|
||||
font-size: 180px; /* Slightly adjusted for better fit in circle */
|
||||
}
|
||||
|
||||
.stat-2 .stat-subtext {
|
||||
color: #000000; /* Black text */
|
||||
position: relative;
|
||||
right: auto;
|
||||
top: auto;
|
||||
text-align: center;
|
||||
width: auto;
|
||||
font-size: 50px; /* Slightly adjusted for better fit in circle */
|
||||
}
|
||||
|
||||
/* Stat 3 Styles (Top Center) - A-roll is at y:450 */
|
||||
.stat-3 .black-rectangle {
|
||||
width: 1000px;
|
||||
height: 350px;
|
||||
background-color: #000000;
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
}
|
||||
|
||||
.stat-3 .stat-number {
|
||||
color: #D0021B;
|
||||
position: absolute;
|
||||
top: 100px;
|
||||
}
|
||||
|
||||
.stat-3 .stat-subtext {
|
||||
color: #FFFFFF;
|
||||
top: 300px;
|
||||
}
|
||||
|
||||
/* Outro Styles */
|
||||
.outro-circle {
|
||||
width: 900px;
|
||||
height: 900px;
|
||||
background-color: #D0021B;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.outro-text {
|
||||
font-size: 150px;
|
||||
font-weight: 900;
|
||||
color: #000000;
|
||||
z-index: 2;
|
||||
text-align: center;
|
||||
width: 1000px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const TRANSCRIPT = [{'text': 'We', 'start': 0.119, 'end': 0.259}, {'text': 'asked', 'start': 0.319, 'end': 0.479}, {'text': 'what', 'start': 0.519, 'end': 0.659}, {'text': 'you', 'start': 0.699, 'end': 0.819}, {'text': 'needed.', 'start': 0.859, 'end': 1.819}, {'text': 'Forty-seven', 'start': 1.86, 'end': 2.299}, {'text': 'percent', 'start': 2.399, 'end': 2.679}, {'text': 'of', 'start': 2.7, 'end': 2.799}, {'text': 'you', 'start': 2.839, 'end': 2.939}, {'text': 'said', 'start': 3.039, 'end': 3.179}, {'text': 'motion', 'start': 3.24, 'end': 3.559}, {'text': 'graphics,', 'start': 3.579, 'end': 4.599}, {'text': 'sixty-two', 'start': 4.679, 'end': 5.179}, {'text': 'percent', 'start': 5.299, 'end': 5.759}, {'text': 'said', 'start': 5.859, 'end': 5.98}, {'text': 'static', 'start': 6.079, 'end': 6.399}, {'text': 'content', 'start': 6.46, 'end': 6.879}, {'text': 'was', 'start': 6.92, 'end': 7.079}, {'text': 'costing', 'start': 7.099, 'end': 7.48}, {'text': 'you', 'start': 7.5, 'end': 7.579}, {'text': 'attention,', 'start': 7.679, 'end': 8.659}, {'text': 'and', 'start': 8.699, 'end': 8.86}, {'text': 'three', 'start': 8.88, 'end': 9.06}, {'text': 'out', 'start': 9.079, 'end': 9.18}, {'text': 'of', 'start': 9.199, 'end': 9.34}, {'text': 'four', 'start': 9.38, 'end': 9.799}, {'text': 'said', 'start': 9.84, 'end': 10.0}, {'text': 'you', 'start': 10.019, 'end': 10.159}, {'text': 'know', 'start': 10.179, 'end': 10.36}, {'text': 'the', 'start': 10.38, 'end': 10.42}, {'text': 'look', 'start': 10.519, 'end': 10.699}, {'text': 'you', 'start': 10.739, 'end': 10.859}, {'text': 'want,', 'start': 10.98, 'end': 11.34}, {'text': 'but', 'start': 11.359, 'end': 11.52}, {'text': "don't", 'start': 11.56, 'end': 11.779}, {'text': 'have', 'start': 11.819, 'end': 11.94}, {'text': 'the', 'start': 11.96, 'end': 12.06}, {'text': 'editing', 'start': 12.079, 'end': 12.4}, {'text': 'skills', 'start': 12.52, 'end': 12.859}, {'text': 'to', 'start': 12.88, 'end': 13.0}, {'text': 'get', 'start': 13.019, 'end': 13.18}, {'text': 'there.', 'start': 13.22, 'end': 14.22}, {'text': 'So', 'start': 14.239, 'end': 14.399}, {'text': 'we', 'start': 14.42, 'end': 14.52}, {'text': 'built', 'start': 14.619, 'end': 14.88}, {'text': 'Editor', 'start': 15.079, 'end': 15.42}, {'text': 'Agent.', 'start': 15.619, 'end': 16.02}];
|
||||
const tl = gsap.timeline({ paused: true });
|
||||
|
||||
// 1. Intro (0-1.8s)
|
||||
tl.set('.intro-container', { opacity: 1 }, 0);
|
||||
tl.from('.black-circle', { scale: 0, duration: 0.3, ease: 'power2.out' }, 0);
|
||||
tl.from('.intro-text', { x: -100, opacity: 0, duration: 0.3, ease: 'power2.out' }, 0.1);
|
||||
tl.set('.intro-container', { opacity: 0 }, 1.8);
|
||||
|
||||
// 2. Stat 1 (1.86s - 4.6s) - "Forty-seven percent"
|
||||
const stat1Time = 1.86;
|
||||
tl.set('.stat-1', { opacity: 1 }, stat1Time);
|
||||
tl.from('.stat-1 .vermillion-square', { x: -1000, duration: 0.3, ease: 'power2.out' }, stat1Time);
|
||||
tl.from('.stat-1 .stat-number', { opacity: 0, scale: 0.8, duration: 0.2, ease: 'power2.out' }, stat1Time + 0.1);
|
||||
tl.from('.stat-1 .stat-subtext', { opacity: 0, y: 20, duration: 0.2, ease: 'power2.out' }, stat1Time + 0.2);
|
||||
tl.set('.stat-1', { opacity: 0 }, 4.6);
|
||||
|
||||
// 3. Stat 2 (4.679s - 8.8s) - "Sixty-two percent"
|
||||
const stat2Time = 4.679;
|
||||
tl.set('.stat-2', { opacity: 1 }, stat2Time);
|
||||
tl.from('.stat-2 .white-circle', { scale: 0, duration: 0.3, ease: 'power2.out' }, stat2Time);
|
||||
tl.set('.stat-2', { opacity: 0 }, 8.8);
|
||||
|
||||
// 4. Stat 3 (8.88s - 14.2s) - "Three out of four"
|
||||
const stat3Time = 8.88;
|
||||
tl.set('.stat-3', { opacity: 1 }, stat3Time);
|
||||
tl.from('.stat-3 .black-rectangle', { width: 0, duration: 0.3, ease: 'power2.out' }, stat3Time);
|
||||
tl.from('.stat-3 .stat-number', { opacity: 0, y: -30, duration: 0.2, ease: 'power2.out' }, stat3Time + 0.1);
|
||||
tl.from('.stat-3 .stat-subtext', { opacity: 0, y: 20, duration: 0.2, ease: 'power2.out' }, stat3Time + 0.2);
|
||||
tl.set('.stat-3', { opacity: 0 }, 14.2);
|
||||
|
||||
// 5. Outro (14.239s - 17s) - "So we built Editor Agent"
|
||||
tl.set('.outro-container', { opacity: 1 }, 14.239);
|
||||
tl.from('.outro-circle', { scale: 0, duration: 0.3, ease: 'power2.out' }, 14.239);
|
||||
tl.from('.outro-text', { scale: 1.5, opacity: 0, duration: 0.3, ease: 'power2.out' }, 14.3);
|
||||
|
||||
window.__timelines["graphics-comp"] = tl;
|
||||
})();
|
||||
</script>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,61 @@
|
||||
# HyperFrames Design Review
|
||||
|
||||
## First Impression
|
||||
This looks like a PowerPoint presentation from a 2010 corporate retreat that's trying way too hard to be "modern." It’s a chaotic mess of geometric shapes and primary colors that lacks any sense of sophisticated art direction.
|
||||
|
||||
---
|
||||
|
||||
## CRITICAL Design Failures
|
||||
|
||||
Issues that make this look unprofessional or straight-up ugly. These MUST be fixed.
|
||||
|
||||
### The "Primary School" Color Palette
|
||||
**Where:** `compositions/graphics.html`
|
||||
**What's wrong:** You're using `#D0021B` (a generic, aggressive red), pure black `#000000`, and pure white `#FFFFFF`. It’s the most basic, uninspired color combination possible. It lacks depth, vibration, or any sense of modern aesthetic.
|
||||
**Why it matters:** It looks cheap and amateur. High-end design uses nuanced tones, not the default colors from a 1995 Windows paint bucket.
|
||||
**Fix it:** Move to a more sophisticated palette. Try a deep crimson or a vibrant coral instead of that "stop sign" red. Use an off-white or a very light gray (`#F5F5F5`) instead of pure white to reduce eye strain and look more "editorial."
|
||||
|
||||
### Typography Hierarchy is Non-Existent
|
||||
**Where:** `compositions/graphics.html` and `compositions/captions.html`
|
||||
**What's wrong:** Everything is just "BIG AND BOLD." You're using `font-weight: 900` for everything from the 200px stats to the 56px captions. When everything is shouting, nothing is heard.
|
||||
**Why it matters:** There's no visual path for the eye to follow. The viewer is just bombarded with heavy blocks of text.
|
||||
**Fix it:** Introduce contrast in weight. Use a lighter weight (300 or 400) for subtext and captions to let the big numbers actually pop. Vary the tracking (letter-spacing) more intentionally.
|
||||
|
||||
### The "Floating Box" Caption Aesthetic
|
||||
**Where:** `compositions/captions.html`
|
||||
**What's wrong:** A solid black rectangle with white text slapped on the bottom. It’s the "default" look of every low-budget social media video.
|
||||
**Why it matters:** It obscures the composition and looks like an afterthought rather than an integrated design element.
|
||||
**Fix it:** Remove the solid black background. Use a subtle text shadow or a very slight gradient overlay if readability is an issue. Or, better yet, integrate the captions into the geometric style of the rest of the video.
|
||||
|
||||
---
|
||||
|
||||
## Design Improvements
|
||||
|
||||
Things that aren't broken but are boring, lazy, or could be significantly better.
|
||||
|
||||
### Robotic Motion Curves
|
||||
**Where:** `index.html` and `compositions/graphics.html`
|
||||
**The problem:** You're using `power2.out` for almost everything. It’s the "safe" choice, which makes it the "boring" choice. The transitions feel mechanical and predictable.
|
||||
**Make it better:** Use more expressive easing. Try `expo.out` for faster, snappier entrances, or `back.out(1.7)` for a slight overshoot that gives the geometric shapes some "weight" and personality.
|
||||
|
||||
### Static Background Void
|
||||
**Where:** `index.html` (`#bg-shapes`)
|
||||
**The problem:** You have a layer for background shapes that is completely empty. The video just sits on a flat white or black void when it's scaled down.
|
||||
**Make it better:** Actually use that layer. Add some subtle, slow-moving geometric patterns or a slight grain texture to give the composition some "atmosphere" so the A-roll doesn't look like it's floating in a vacuum.
|
||||
|
||||
---
|
||||
|
||||
## What Actually Works
|
||||
|
||||
The layout shifts in `index.html` (moving the A-roll from right to left to center) show a basic understanding of composition and screen real estate. It’s the only thing keeping this from being a total disaster. The timing of the transitions (0.3s) is also decent—it’s quick enough to keep the pace up.
|
||||
|
||||
---
|
||||
|
||||
## Design Verdict
|
||||
|
||||
**Visual Impact:** 3/10 - It’s loud, but not in a good way. It’s the visual equivalent of someone shouting "LOOK AT ME" while wearing a neon vest.
|
||||
**Color & Typography:** 2/10 - Lazy, default choices that scream "I didn't want to spend time on this."
|
||||
**Motion & Animation Feel:** 4/10 - Functional but generic. No soul, no rhythm.
|
||||
**Overall Aesthetic:** 3/10 - Amateur corporate template vibes.
|
||||
|
||||
**Bottom Line:** I wouldn't show this to a client unless I wanted to be fired. It needs a complete overhaul of its visual identity—start by picking a real color palette and learning that "bold" isn't the only font weight that exists.
|
||||
@@ -0,0 +1,130 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Geometric Bold - Ikko Tanaka Style</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
|
||||
<style>
|
||||
body, html {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 1920px;
|
||||
height: 1080px;
|
||||
overflow: hidden;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
#main-canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.aroll-container {
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
border-radius: 12px;
|
||||
background: #000;
|
||||
}
|
||||
|
||||
#aroll-video {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
/* Background Shapes Layer */
|
||||
#bg-shapes {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="main-canvas" data-composition-id="main-video" data-width="1920" data-height="1080" data-duration="17">
|
||||
|
||||
<video id="aroll-video"
|
||||
data-start="0"
|
||||
data-track-index="1"
|
||||
class="aroll-container"
|
||||
style="width: 1920px; height: 1080px; top: 0; left: 0; position: absolute; overflow: hidden; border-radius: 12px; object-fit: cover; background: #000;"
|
||||
src="https://gen-os-static.s3.us-east-2.amazonaws.com/astral_assets/uploaded_assets/743011f1_e3c6afe3b7aa42369ce01e4961c60e22.mp4">
|
||||
</video>
|
||||
|
||||
<!-- Background Shapes Layer -->
|
||||
<div id="bg-shapes" data-composition-id="bg-layer" data-width="1920" data-height="1080" data-start="0" data-duration="17" data-track-index="0">
|
||||
<script>
|
||||
const bgTl = gsap.timeline({ paused: true });
|
||||
window.__timelines["bg-layer"] = bgTl;
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<!-- Intro / Graphics Layer -->
|
||||
<div id="graphics-layer"
|
||||
data-composition-id="graphics-comp"
|
||||
data-composition-src="compositions/graphics.html"
|
||||
data-start="0"
|
||||
data-duration="17"
|
||||
data-track-index="2">
|
||||
</div>
|
||||
|
||||
<!-- Captions Layer -->
|
||||
<div id="captions-layer"
|
||||
data-composition-id="captions-comp"
|
||||
data-composition-src="compositions/captions.html"
|
||||
data-start="0"
|
||||
data-duration="17"
|
||||
data-track-index="3">
|
||||
</div>
|
||||
|
||||
<!-- Transition SFX -->
|
||||
<audio id="click-sfx" data-start="0" data-track-index="4" src="https://gen-os-static.s3.us-east-2.amazonaws.com/astral_assets/generated_assets/853dda23_70261e8b676444bfbfe147e34803851e.mp3"></audio>
|
||||
|
||||
<script>
|
||||
const tl = gsap.timeline({ paused: true });
|
||||
const aroll = document.getElementById('aroll-video');
|
||||
|
||||
// --- A-ROLL FRAMING LOGIC (LANDSCAPE 1920x1080) ---
|
||||
// 0-3s: Intro - Slide in from right & Scale. Full screen.
|
||||
tl.fromTo(aroll,
|
||||
{ x: 1920, scale: 0.8 },
|
||||
{ x: 0, scale: 1, duration: 0.3, ease: "power2.out" }, 0);
|
||||
|
||||
// Moment 1: 1.86s (Transition to first stat: "Forty-seven percent")
|
||||
// Scale down to 60%, reposition to right side (next to graphic)
|
||||
tl.to(aroll, {
|
||||
width: 1152, height: 648, x: 700, y: 216,
|
||||
duration: 0.3, ease: "power2.out"
|
||||
}, 1.86);
|
||||
|
||||
// Moment 2: 4.679s (Transition to second stat: "Sixty-two percent")
|
||||
// Shift to left side (next to graphic)
|
||||
tl.to(aroll, {
|
||||
x: 68, y: 216,
|
||||
duration: 0.3, ease: "power2.out"
|
||||
}, 4.679);
|
||||
|
||||
// Moment 3: 8.88s (Transition to third stat: "Three out of four")
|
||||
// Center and scale down (graphic will be above/below)
|
||||
tl.to(aroll, {
|
||||
width: 960, height: 540, x: 480, y: 450,
|
||||
duration: 0.3, ease: "power2.out"
|
||||
}, 8.88);
|
||||
|
||||
// Moment 4: 14.239s (Closing/Editor Agent Reveal: "So we built...")
|
||||
// Full screen zoom center
|
||||
tl.to(aroll, {
|
||||
width: 1920, height: 1080, x: 0, y: 0, scale: 1,
|
||||
duration: 0.3, ease: "power2.out"
|
||||
}, 14.239);
|
||||
|
||||
window.__timelines["main-video"] = tl;
|
||||
</script>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user