The Journey from Concept to Cinematic Intro Sequence
Versions 11 → 11a → 11b → 11c → 11d → 11e
This stage introduces the concept of sequential timing - making multiple events happen one after another with precise control. The challenge is ensuring each phrase:
This is a common pattern in game development, interactive media, and user interface design.
Goal: Add three intro phrases with delays
Status: ❌ BROKEN - Timing Bug
Issue: Delays were not properly cumulative. Each phrase's start time wasn't accounting for the DURATION of previous phrases, only their delays.
Problem: Original timing calculation:
phrase2StartTime = phrase1StartTime + DELAY_2
This doesn't account for how long phrase 1 RUNS (3000ms). Phrases would overlap!
Correct Formula:
phrase2StartTime = phrase1StartTime + TITLE_DURATION_MS + DELAY_2
This ensures phrase 2 waits for phrase 1 to finish (TITLE_DURATION_MS) PLUS the delay.
Goal: Fix timing calculations
Status: ❌ FILE CORRUPTED
Issue: File corruption occurred around line 1032, causing multiple syntax errors.
Location: Line 1032 in drawBackgroundWithTrails() function
Symptom: Missing code in the else block:
} else {
// MISSING: background(0, 0, 0);
// MISSING: updateAndDrawStars();
}
Impact: Function incomplete, leading to cascading syntax errors throughout file.
Added back the essential background clearing and star rendering:
} else {
// No trail effect - draw clean background
background(0, 0, 0);
updateAndDrawStars();
}
Also removed duplicate code blocks in initialization functions.
Post-Fix Status: ⚠️ Still Not Working - Corruption fixed, but intro phrases don't appear!
Goal: Implement correct timing with TITLE_DURATION_MS
Status: ⚠️ Working Timing, Still No Display
Created By: Copying fixed 11a and applying 8 timing corrections
The correct sequential timing pattern:
// First phrase starts after initial delay
phrase1StartTime = DELAY_1; // 500ms
// Second phrase waits for phrase 1 to finish + gap
phrase2StartTime = phrase1StartTime + TITLE_DURATION_MS + DELAY_2;
// = 500 + 3000 + 250 = 3750ms
// Third phrase waits for phrase 2 to finish + gap
phrase3StartTime = phrase2StartTime + TITLE_DURATION_MS + DELAY_3;
// = 3750 + 3000 + 250 = 7000ms
// Title waits for phrase 3 to finish + gap
titleStartTime = phrase3StartTime + TITLE_DURATION_MS + DELAY_4;
// = 7000 + 3000 + 100 = 10100ms
Each start time is the sum of ALL previous animations and delays.
Problem: Even with perfect timing, the intro phrases still don't appear on screen! Time to debug...
Strategy: Comment out everything except the FIRST intro phrase to isolate the issue
Status: ✅ CRITICAL BUGS DISCOVERED AND FIXED!
Original Code:
let introPhrase1 = "Tech Novice Tools"; // WRONG!
let introPhrase2 = "& Copilot"; // WRONG!
let introPhrase3 = "Present"; // WRONG!
Problem: These are strings, not objects! When code tries to check introPhrase1.active, it fails because strings don't have an active property.
Correct Code:
let introPhrase1 = null; // Will be created by initializeIntroPhrases()
let introPhrase2 = null; // Will be created by initializeIntroPhrases()
let introPhrase3 = null; // Will be created by initializeIntroPhrases()
Original Logic in draw():
if (introPhrase1 && introPhrase1.active) {
updateTitleObject(introPhrase1); // Check timing and activate
displayTitleObject(introPhrase1); // Display if active
}
The Problem: This creates a paradox!
if statement only runs updateTitleObject() when active is trueactive is SET TO TRUE inside updateTitleObject()!active starts as false, updateTitleObject() NEVER RUNSupdateTitleObject() never runs, active NEVER BECOMES TRUEThis is a classic state management bug! We're checking a condition that can only become true if we run the code that checks the condition!
New Logic:
if (introPhrase1) {
// ALWAYS check timing (this can activate the phrase)
updateTitleObject(introPhrase1);
// ONLY display if active
if (introPhrase1.active) {
displayTitleObject(introPhrase1);
}
}
Why This Works:
updateTitleObject() runs EVERY FRAME to check elapsed timeactive = trueif statement displays the phraseResult: TNT phrase appears! ✅ SUCCESS!
This bug teaches an important principle in programming:
Never gate the function that SETS a condition behind checking that same condition.
In other words:
if (isReady) { checkIfReady(); } ← Chicken-and-egg!checkIfReady(); if (isReady) { doSomething(); } ← Always check, conditionally actThis pattern appears everywhere:
Goal: Test all three intro phrases together
Status: ✅ ALL PHRASES WORKING!
Configuration: Title and credits still commented out for testing
Timeline Achieved:
Result: Beautiful sequential intro! Each phrase appears, zooms, and fades before the next begins.
Goal: Re-enable title and student credits for full experience
Status: ✅ COMPLETE AND WORKING!
Full Timeline:
Final Result: A complete cinematic intro sequence perfectly synchronized with the Superman theme music!
This unified function creates title-like objects (intro phrases and main title):
function createTitleObject(text, delay) {
return {
text: text, // The text to display
size: TITLE_INITIAL_SIZE, // Start small (8 pixels)
opacity: 100, // Full opacity
active: false, // Not active yet
blurActive: false, // Blur effect not started
startTime: delay, // When to activate (ms from start)
hasCompleted: false // Track if animation finished
};
}
This function checks timing and manages the phrase lifecycle:
function updateTitleObject(titleObj) {
if (!titleObj.active && !titleObj.hasCompleted) {
// Check if it's time to activate
let elapsed = millis() - animationStartTime;
if (elapsed >= titleObj.startTime) {
titleObj.active = true; // Activate the phrase!
console.log("🔵 Phrase activated:", titleObj.text);
}
return; // Not active yet, nothing to update
}
if (titleObj.active && !titleObj.hasCompleted) {
// Grow the text
titleObj.size += (titleObj.blurActive ?
TITLE_ACCELERATED_GROWTH : TITLE_GROWTH_RATE);
// Start blur when size reaches threshold
if (titleObj.size >= TITLE_BLUR_START_SIZE) {
titleObj.blurActive = true;
}
// Fade out during blur
if (titleObj.blurActive) {
titleObj.opacity -= CREDIT_FADE_SPEED * 2;
}
// Mark as completed when fully faded
if (titleObj.opacity <= 0) {
titleObj.active = false;
titleObj.hasCompleted = true;
}
}
}
The delay constants make timing adjustments easy:
// Delay before first phrase (ms)
const DELAY_1 = 500;
// Delay after phrase 1 completes before phrase 2 starts (ms)
const DELAY_2 = 250;
// Delay after phrase 2 completes before phrase 3 starts (ms)
const DELAY_3 = 250;
// Delay after phrase 3 completes before title starts (ms)
const DELAY_4 = 100;
// Each phrase/title animates for this duration (ms)
const TITLE_DURATION_MS = 3000;
// Pause between title exit and first credit (ms)
const CREDITS_PAUSE = 11500;
This stage demonstrated a professional debugging workflow:
Key Takeaway: When facing a complex bug, simplify the problem by isolating components until you find the issue.
Version 11e delivers a complete cinematic experience: