๐ Stage 7a Overview
Stage 7a solves a visual quality problem that emerged from the motion trail system introduced in Stage 7. When semi-transparent overlays accumulate over time, they can create muddy artifacts in the background. This experimental version tests a background refresh mechanism that clears these artifacts when each new student credit appears.
โจ Result: The background refresh feature works well! Each student name now appears on a clean canvas, eliminating artifact buildup while maintaining the dramatic trail effects during each credit's animation.
๐ค User Request #13 - Stage 7a: Background Artifacts
Problem Observed: "There are some weird artifacts in the black color since the opacity of the fill color isn't completely solid."
Root Cause:
- Stage 7 uses semi-transparent overlays for motion trails
- Each frame draws a translucent black rectangle to fade previous frames
- Over time, these overlays accumulate imperfectly
- Text with opacity < 100 doesn't fully cover underlying artifacts
- Result: Background looks "dirty" or has strange color variations
Proposed Solution: "I'm thinking a pause variable with a quick refresh of background will do the trick, but this is a test. If it fails we can go back to version 7 and start again."
Experimental Approach:
- Create version 7a as a test (can revert if needed)
- Implement background refresh before each new student appears
- Brief pause with clean background
- Evaluate whether this solves the artifact problem
๐ค Implementation - Configuration Variables
Added two new configuration variables to control the refresh behavior:
// ============================================
// STAGE 7a: BACKGROUND REFRESH CONFIGURATION
// ============================================
// *** BACKGROUND REFRESH SETTINGS ***
// Controls whether to fully clear background when new credits appear
// Enable background refresh before new credits
// true = clear artifacts, false = keep accumulated trails
const REFRESH_ON_NEW_CREDIT = true;
// Number of frames to show clean background before new credit
// Gives a brief "pause" with clean slate
// Try: 1 (instant), 5 (brief pause), 10 (noticeable pause)
const REFRESH_PAUSE_FRAMES = 3;
๐ Design Decisions:
REFRESH_ON_NEW_CREDIT (boolean):
- true = Feature enabled (default for 7a)
- false = Disable refresh, behave like 7
- Makes it easy to compare with/without refresh
REFRESH_PAUSE_FRAMES (integer):
- Default: 3 frames โ 50ms at 60fps
- 1 frame: Nearly instant, barely perceptible
- 3 frames: Brief clean moment, smooth transition
- 5 frames: Noticeable pause, creates rhythm
- 10+ frames: Feels like interruption, too long
Why 3 frames chosen: Balances artifact clearing with smooth flow
๐ค Implementation - Tracking Variables
Added global variables to track refresh state:
// *** STAGE 7a: Refresh tracking ***
let refreshCountdown = 0; // Counts down frames during background refresh
let lastActivatedCreditIndex = -1; // Track which credit just activated
๐ Variable Purposes:
refreshCountdown:
- Counts down from REFRESH_PAUSE_FRAMES to 0
- When > 0: Skip normal rendering, show clean background
- When = 0: Resume normal trail effect
- Acts as a timer for the refresh duration
lastActivatedCreditIndex:
- Remembers the index of the most recent active credit
- Starts at -1 (no credits active yet)
- Increments when new credit becomes active
- Prevents triggering refresh multiple times for same credit
- Example: When credit[0] activates, sets to 0
๐ค Implementation - Detection Logic
Created checkForNewCredits() function to detect credit activation:
/**
* STAGE 7a: Check if any new credit just became active
* If so, trigger a background refresh to clear artifacts
*/
function checkForNewCredits() {
if (!REFRESH_ON_NEW_CREDIT) return; // Feature disabled
for (let i = 0; i < credits.length; i++) {
// Check if this credit just became active
if (credits[i].active && i > lastActivatedCreditIndex) {
// New credit detected!
lastActivatedCreditIndex = i;
refreshCountdown = REFRESH_PAUSE_FRAMES;
console.log(`๐งน Refreshing background for credit ${i}: ${credits[i].name}`);
break; // Only trigger once per frame
}
}
}
๐ How Detection Works:
Step-by-step logic:
- Check feature flag: If REFRESH_ON_NEW_CREDIT is false, do nothing
- Loop through credits: Examine each credit in order
- Check conditions: Is credit active AND is its index greater than last?
- If both true: This is a newly activated credit!
- Update tracker: Set lastActivatedCreditIndex to current index
- Start countdown: Set refreshCountdown to REFRESH_PAUSE_FRAMES
- Log event: Console message for debugging
- Break loop: Only process one new credit per frame
Why "i > lastActivatedCreditIndex"?
- Ensures we only trigger for NEW activations
- Credits activate in order: 0, then 1, then 2, etc.
- Once credit 0 is handled, we only care about 1+
- Prevents re-triggering for already-active credits
Example timeline:
- Frame 210: Credit 0 becomes active, lastActivatedCreditIndex = 0, countdown = 3
- Frames 211-213: Countdown running (3โ2โ1โ0)
- Frame 510: Credit 1 becomes active, lastActivatedCreditIndex = 1, countdown = 3
- And so on...
๐ค Implementation - Modified Draw Loop
Updated draw() function to handle refresh countdown:
function draw() {
// *** STAGE 7a: Check if we need to refresh background ***
if (refreshCountdown > 0) {
// During refresh: show clean background with stars
background(0, 0, 0);
drawStars();
refreshCountdown--;
return; // Skip drawing credits during refresh
}
// *** STAGE 7: Draw background with trail effect ***
drawBackgroundWithTrails();
// Draw and update movie title if it's active
if (movieTitle && movieTitle.active) {
updateMovieTitle(movieTitle);
displayMovieTitle(movieTitle);
}
// *** STAGE 7a: Check for newly activated credits ***
checkForNewCredits();
// Draw and update each credit
for (let i = 0; i < credits.length; i++) {
updateCredit(credits[i]);
displayCredit(credits[i]);
}
}
๐ Draw Loop Flow Control:
Early return pattern:
- Check countdown FIRST, before any other rendering
- If countdown > 0: Draw clean background, decrement, EXIT
- Early return prevents normal rendering during refresh
- Credits continue updating in background (time still passes)
Frame-by-frame during refresh:
- Frame 1: countdown = 3 โ draw clean, countdown = 2, return
- Frame 2: countdown = 2 โ draw clean, countdown = 1, return
- Frame 3: countdown = 1 โ draw clean, countdown = 0, return
- Frame 4: countdown = 0 โ normal rendering resumes!
Why check for new credits AFTER background?
- Credits update their active state in updateCredit()
- We need to check activation after they update
- But before they render (so we can skip their first frame)
- Timing ensures clean background ready when credit appears
๐ Results & Analysis
โ
Experiment Success!
User Feedback: "This is working well, let's keep it"
What Improved:
- โ
Background artifacts eliminated
- โ
Each credit appears on clean canvas
- โ
No more "muddy" accumulated overlays
- โ
Trails still work beautifully during animation
- โ
Brief pause creates nice rhythm between credits
- โ
Overall visual quality significantly improved
Why It Works:
- Full
background(0, 0, 0) clears ALL accumulated layers
- 3 frames = enough time to fully clear without feeling slow
- Stars redrawn fresh = always crisp and bright
- Credits get pristine canvas to start their journey
- Trails still accumulate DURING each credit (desired effect)
- But cleared BETWEEN credits (prevents artifact buildup)
๐ฌ Technical Deep Dive: Why Artifacts Occurred
๐ Understanding Semi-Transparent Overlays:
The Math of Opacity Accumulation:
- Each frame: Draw rectangle with alpha = fadeAlpha (from TRAIL_FADE_AMOUNT)
- TRAIL_FADE_AMOUNT = 10 โ fadeAlpha โ 4 (in HSB 0-100 scale)
- This means each overlay blocks ~4% of light
- Over 25 frames: 1 - (0.96^25) โ 64% darkening
- But floating-point rounding creates imperfections!
Compounding Errors:
- Frame 1: Draw text at opacity 100 on black (0,0,0)
- Frame 2: Overlay alpha=4 โ text now ~96 visible
- Frame 3: Overlay again โ text now ~92 visible
- Frame 10: Text has faded but background isn't pure black
- Problem: Background might be (0, 0, 0.5) or (0, 0, 1)
- Result: Very dark gray, not pure black
Why New Text Shows Artifacts:
- New text drawn with opacity < 100 (starts fading immediately)
- Semi-transparent text blends with "almost black" background
- Creates unexpected color shifts
- Especially visible with colored text (HSB hue affects blend)
Why Full Clear Fixes It:
background(0, 0, 0) = pure black, no ambiguity
- Resets accumulated rounding errors
- Provides known baseline for new rendering
- Like "clearing the slate" in math class!
๐ Learning Outcomes: Stage 7a
๐ Software Engineering Principles:
1. Experimental Problem-Solving:
- Identify problem: Artifacts in background
- Form hypothesis: Clearing background will help
- Design experiment: Version 7a with refresh feature
- Implement safeguards: Can revert to 7 if it fails
- Test and evaluate: Does it work?
- Conclusion: Success! Keep the changes
2. Defensive Programming:
- Feature flag: REFRESH_ON_NEW_CREDIT for easy disable
- Version control: 7a as separate file (can revert)
- Console logging: Track when refresh happens
- Configurable timing: REFRESH_PAUSE_FRAMES adjustable
3. State Management:
- Track multiple states: refreshCountdown, lastActivatedCreditIndex
- Coordinate timing: When to refresh vs. render
- Prevent duplicate triggers: Only activate once per credit
- Clean state on reset: Both variables reset in mousePressed/keyPressed
4. Graphics Programming Concepts:
- Frame persistence vs. clearing
- Opacity accumulation and artifacts
- Floating-point precision issues in graphics
- Importance of known baseline (pure black background)
๐งช Comparison: Stage 7 vs 7a
๐ Side-by-Side Feature Comparison:
| Feature |
Stage 7 |
Stage 7a |
| Stroke & Fill Controls |
โ
Yes |
โ
Yes (same) |
| Motion Trails |
โ
Yes |
โ
Yes (same) |
| Background Artifacts |
โ Accumulate over time |
โ
Cleared between credits |
| Visual Quality |
โ ๏ธ Gets "muddy" |
โ
Clean and crisp |
| New Variables |
9 total |
11 total (+2 refresh) |
| New Functions |
applyTextStyle() |
+ checkForNewCredits() |
| Complexity |
Moderate |
Slightly higher |
| Recommended? |
โ Use 7a instead |
โ
Current best version! |
Bottom Line: Stage 7a solves 7's artifact problem with minimal added complexity. The refresh mechanism is elegant and effective!
๐งช Student Experiments & Activities
๐ Challenge 1: Timing Exploration
Task: Find the optimal REFRESH_PAUSE_FRAMES value
- Try values: 1, 3, 5, 10, 15, 30
- At 60fps: 1 frame = 17ms, 3 frames = 50ms, 10 frames = 167ms
- Which feels most natural?
- Which is too fast (jarring)?
- Which is too slow (feels broken)?
Extension: Poll the class - what's the consensus?
๐ Challenge 2: Conditional Refresh
Task: Only refresh if trails have accumulated
// Advanced: Track how many frames since last refresh
let framesSinceRefresh = 0;
// Only refresh if enough frames have passed
if (credits[i].active && i > lastActivatedCreditIndex) {
if (framesSinceRefresh > 100) { // Only if trails built up
refreshCountdown = REFRESH_PAUSE_FRAMES;
framesSinceRefresh = 0;
}
lastActivatedCreditIndex = i;
}
Question: Does this improve or hurt the effect?
๐ Challenge 3: A/B Testing
Task: Scientific comparison of 7 vs 7a
- Open both versions side-by-side
- Run simultaneously
- Document observations:
- When do artifacts appear in version 7?
- How does 7a prevent them?
- Is the refresh noticeable in 7a?
- Which looks more professional?
- Create comparison chart/presentation
๐ Challenge 4: Debug Visualization
Task: Add visual indicator during refresh
if (refreshCountdown > 0) {
background(0, 0, 0);
drawStars();
// NEW: Show refresh indicator
push();
fill(60, 100, 100, 50); // Yellow, semi-transparent
noStroke();
textSize(12);
text(`Refreshing... ${refreshCountdown}`, width/2, 20);
pop();
refreshCountdown--;
return;
}
Purpose: Visualize what's happening "behind the scenes"
๐ Challenge 5: Alternative Solutions
Task: Brainstorm other ways to solve artifacts
Ideas to explore:
- Increase TRAIL_FADE_AMOUNT (faster fade = fewer artifacts)
- Use multiple overlay passes per frame
- Track "dirt level" and clear when threshold reached
- Selective clearing (only clear center area, keep edges)
- Gradual clear (fade in clean background over multiple frames)
Question: Would any of these work better than 7a's approach?
โ
Stage 7a Complete & Successful!
๐ฌ What Makes Stage 7a Special:
Problem-Solving Success:
- Identified visual artifact problem
- Designed experimental solution
- Implemented with safety nets (can revert)
- Tested and validated effectiveness
- Result: Problem solved! โจ
Technical Achievement:
- Smart state tracking (countdown, last index)
- Precise timing control (frame-level)
- Elegant integration with existing code
- Minimal performance impact
- Configurable and debuggable
Educational Value:
- Demonstrates iterative development
- Shows experimental approach to problem-solving
- Teaches importance of visual quality
- Illustrates graphics programming challenges
- Models scientific method in coding
New Features Added:
- REFRESH_ON_NEW_CREDIT - Enable/disable refresh
- REFRESH_PAUSE_FRAMES - Control refresh duration
- refreshCountdown - Track refresh state
- lastActivatedCreditIndex - Prevent duplicate triggers
- checkForNewCredits() - Detection function
- Modified draw() - Refresh handling
Complete Evolution (Stages 1-7a):
- โ
Stage 1: Basic animation, starfield, credits
- โ
Stage 2: Configuration, tilt effects
- โ
Stage 3: Blur/explosion on exit
- โ
Stage 4: Flying movie title
- โ
Stage 5: Title acceleration and effects
- โ
Stage 6: Credits pause for timing
- โ
Stage 7: Stroke/fill controls + motion trails
- โ
Stage 7a: Background refresh = artifact-free! ๐งนโจ
Files Created:
- ๐ movieCreditsSim7a.html - Clean, professional animation
- ๐ chatLog7a.html - Complete documentation
๐ Stage 7a proves that thoughtful problem-solving and experimentation lead to better software. The refresh feature is a small addition with a big impact on quality!
๐ What's Next?
Possible Future Enhancements:
- Variable refresh timing based on trail accumulation
- Smooth fade-in of clean background (gradual clear)
- Different refresh styles (iris wipe, fade, instant)
- Per-credit refresh control (some skip refresh)
- Adaptive refresh based on performance/frame rate
Other Feature Ideas:
- Sound effects synchronized with animation
- Multiple color themes/palettes
- User controls (play/pause/speed)
- Export as video
- Custom fonts
- 3D perspective effects
Teaching Opportunities:
- Compare all versions (1-7a) to see evolution
- Student presentations on favorite features
- Code review sessions analyzing design decisions
- Performance profiling and optimization
- Portfolio pieces - students customize and showcase
The journey continues - keep experimenting! ๐งน๐ปโจ