First, we defined what content should be customizable:
const contentStructure = {
// Intro sequence (3 phrases before title)
introPhrase1: "Tech Novice Tools",
introPhrase2: "& Copilot",
introPhrase3: "Present",
// Main title (supports multi-line with \n)
movieTitle: "Learning CS\\nwith AI",
// Array of student names
students: [
"Alice Johnson",
"Bob Smith",
"Charlie Davis",
"Diana Martinez",
"Evan Wilson",
"Fiona Brown"
],
// Array of technical concepts
concepts: [
"Variables & Constants",
"Functions & Methods",
"Loops & Iteration",
"Arrays & Lists",
"Objects & Classes",
"Conditionals & Logic",
"Animation Frames",
"Color Theory (HSB)",
"Event Handling",
"State Management",
"Graphics Programming",
"Audio Integration",
"Data Persistence",
"Configuration Systems",
"Sequential Timing"
]
};
This demonstrates the MVC (Model-View-Controller) pattern:
By separating these concerns, we can change content without affecting visual effects, and vice versa.
1. Text Area Editors:
Created multi-line text areas for bulk editing:
<div class="editor-panel">
<h3>đ Student Names</h3>
<p class="count-display">Total Students: <span id="studentCount">0</span></p>
<textarea id="studentNamesEditor" class="name-input" rows="10"
placeholder="Enter student names, one per line..."></textarea>
<div class="help-text">
đĄ Enter one name per line. Empty lines will be ignored.
</div>
</div>
2. Intro Phrase Inputs:
Single-line inputs for the three intro phrases:
<input type="text" id="phrase1Input" class="name-input"
placeholder="First phrase (e.g., 'Tech Novice Tools')">
<input type="text" id="phrase2Input" class="name-input"
placeholder="Second phrase (e.g., '& Copilot')">
<input type="text" id="phrase3Input" class="name-input"
placeholder="Third phrase (e.g., 'Present')">
3. Movie Title with Multi-line Support:
<textarea id="movieTitleInput" class="name-input" rows="3"
placeholder="Movie title (use \n for line breaks)"></textarea>
<div class="help-text">
đĄ Use \n to create line breaks. Example: "Learning CS\nwith AI"
</div>
4. Action Buttons:
<button class="btn-custom btn-save" onclick="saveContent()">
đž Save Content
</button>
<button class="btn-custom btn-load" onclick="loadContent()">
đ Load Saved Content
</button>
<button class="btn-custom btn-reset" onclick="resetToDefaults()">
đ Reset to Defaults
</button>
Student Names Parser:
function getStudentNames() {
const editor = document.getElementById('studentNamesEditor');
const text = editor.value;
// Split by newlines, trim whitespace, filter empty lines
const names = text
.split('\n')
.map(name => name.trim())
.filter(name => name.length > 0);
return names;
}
Technical Concepts Parser:
function getTechnicalConcepts() {
const editor = document.getElementById('technicalConceptsEditor');
const text = editor.value;
const concepts = text
.split('\n')
.map(concept => concept.trim())
.filter(concept => concept.length > 0);
return concepts;
}
Live Count Updates:
function updateCounts() {
const studentCount = getStudentNames().length;
const conceptCount = getTechnicalConcepts().length;
document.getElementById('studentCount').textContent = studentCount;
document.getElementById('conceptCount').textContent = conceptCount;
}
// Update counts as user types
document.getElementById('studentNamesEditor')
.addEventListener('input', updateCounts);
document.getElementById('technicalConceptsEditor')
.addEventListener('input', updateCounts);
This code demonstrates powerful JavaScript array methods:
.split('\n') - Convert string to array by splitting on newlines.map() - Transform each element (trim whitespace).filter() - Keep only non-empty stringsThese functional programming techniques make code concise and readable.
Save Content to localStorage:
function saveContent() {
const content = {
// Intro phrases
introPhrase1: document.getElementById('phrase1Input').value.trim(),
introPhrase2: document.getElementById('phrase2Input').value.trim(),
introPhrase3: document.getElementById('phrase3Input').value.trim(),
// Movie title (preserve \n as \\n for storage)
movieTitle: document.getElementById('movieTitleInput').value.trim(),
// Arrays
students: getStudentNames(),
concepts: getTechnicalConcepts()
};
try {
localStorage.setItem('movieCreditsContent', JSON.stringify(content));
showStatus('â
Content saved successfully!', 'success');
console.log('đž Saved content:', content);
} catch (error) {
showStatus('â Error saving content: ' + error.message, 'error');
}
}
Load Content from localStorage:
function loadContent() {
try {
const saved = localStorage.getItem('movieCreditsContent');
if (!saved) {
showStatus('âšī¸ No saved content found', 'info');
return;
}
const content = JSON.parse(saved);
// Populate intro phrases
document.getElementById('phrase1Input').value = content.introPhrase1 || '';
document.getElementById('phrase2Input').value = content.introPhrase2 || '';
document.getElementById('phrase3Input').value = content.introPhrase3 || '';
// Populate movie title
document.getElementById('movieTitleInput').value = content.movieTitle || '';
// Populate student names (join array with newlines)
document.getElementById('studentNamesEditor').value =
(content.students || []).join('\n');
// Populate technical concepts
document.getElementById('technicalConceptsEditor').value =
(content.concepts || []).join('\n');
updateCounts();
showStatus('â
Content loaded successfully!', 'success');
} catch (error) {
showStatus('â Error loading content: ' + error.message, 'error');
}
}
Reset to Factory Defaults:
function resetToDefaults() {
const defaults = {
introPhrase1: "Tech Novice Tools",
introPhrase2: "& Copilot",
introPhrase3: "Present",
movieTitle: "Learning CS\\nwith AI",
students: [
"Alice Johnson",
"Bob Smith",
"Charlie Davis",
"Diana Martinez",
"Evan Wilson",
"Fiona Brown"
],
concepts: [
"Variables & Constants",
"Functions & Methods",
"Loops & Iteration",
// ... (15 total concepts)
]
};
// Populate editor with defaults
populateEditor(
defaults.students,
defaults.concepts,
defaults.introPhrase1,
defaults.introPhrase2,
defaults.introPhrase3,
defaults.movieTitle
);
showStatus('đ Reset to default content', 'info');
}
1. Load Both Configurations:
// *** STAGE 14: CONFIGURATION WITH LOCALSTORAGE ***
let savedConfig = null;
try {
const saved = localStorage.getItem('movieCreditsConfig');
if (saved) {
savedConfig = JSON.parse(saved);
console.log('đ Loaded effects config:', savedConfig);
}
} catch (error) {
console.warn('â ī¸ Error loading effects config:', error);
}
// *** STAGE 15: CONTENT CUSTOMIZATION ***
let savedContent = null;
try {
const saved = localStorage.getItem('movieCreditsContent');
if (saved) {
savedContent = JSON.parse(saved);
console.log('đ Loaded custom content:', savedContent);
}
} catch (error) {
console.warn('â ī¸ Error loading content:', error);
}
2. Apply Custom Intro Phrases:
// *** INTRO PHRASE TEXTS ***
// Stage 15: Can be customized via customizeContent utility
const INTRO_PHRASE_1 = savedContent?.introPhrase1 || "Tech Novice Tools";
const INTRO_PHRASE_2 = savedContent?.introPhrase2 || "& Copilot";
const INTRO_PHRASE_3 = savedContent?.introPhrase3 || "Present";
3. Apply Custom Movie Title:
// *** MOVIE TITLE ***
// Stage 15: Can be customized via customizeContent utility
// Convert stored \\n back to actual newline for p5.js text() function
const MOVIE_TITLE_TEXT = (savedContent?.movieTitle || "Learning CS\\nwith AI")
.replace(/\\n/g, '\n');
Why the \\n to \n conversion?
\n\\n\n.replace(/\\n/g, '\n') to convert back4. Apply Custom Student Names:
// Default student names
const DEFAULT_STUDENTS = [
"Alice Johnson",
"Bob Smith",
"Charlie Davis",
"Diana Martinez",
"Evan Wilson",
"Fiona Brown"
];
// Use custom names if available, otherwise defaults
const studentNames = savedContent?.students || DEFAULT_STUDENTS;
5. Apply Custom Technical Concepts:
// Default technical concepts
const DEFAULT_CONCEPTS = [
"Variables & Constants",
"Functions & Methods",
"Loops & Iteration",
"Arrays & Lists",
"Objects & Classes",
"Conditionals & Logic",
"Animation Frames",
"Color Theory (HSB)",
"Event Handling",
"State Management",
"Graphics Programming",
"Audio Integration",
"Data Persistence",
"Configuration Systems",
"Sequential Timing"
];
// Use custom concepts if available, otherwise defaults
const technicalConcepts = savedContent?.concepts || DEFAULT_CONCEPTS;
6. Generate Credits from Content:
// Generate student credits
for (let i = 0; i < studentNames.length; i++) {
credits.push({
name: studentNames[i], // From custom content
// ... other properties
});
}
// Generate technical concept credits
for (let i = 0; i < technicalConcepts.length; i++) {
credits.push({
name: technicalConcepts[i], // From custom content
isTechnical: true,
// ... other properties
});
}
The original green range (100-140 hue) produced inconsistent colors:
Result: Some technical credits looked washed out or off-color.
Changed color range to focus on vibrant true greens:
// OLD CODE (v14):
const GREEN_HUE_MIN = 100; // Yellow-green
const GREEN_HUE_MAX = 140; // Cyan-green
// NEW CODE (v15):
const GREEN_HUE_MIN = 110; // Lime/grass green
const GREEN_HUE_MAX = 130; // Forest green
Benefits:
In HSB (Hue-Saturation-Brightness) color mode:
By narrowing the hue range from 110-130 (only 20 degrees), we ensure all technical credits stay within the "green family" while still having variety.
Updated navigation across all files to include both customizers:
<nav class="navbar navbar-expand-lg">
<div class="container-fluid">
<a class="navbar-brand" href="#">đŦ Movie Credits Simulator v15</a>
<div class="collapse navbar-collapse">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link" href="movieCreditsSim15.html">đŦ Simulator</a>
</li>
<li class="nav-item">
<a class="nav-link" href="customizeContent1.html">đ Content</a>
</li>
<li class="nav-item">
<a class="nav-link" href="customizeText3.html">âī¸ Effects</a>
</li>
<li class="nav-item">
<a class="nav-link" href="aboutMCreditsApp15.html">âšī¸ About</a>
</li>
</ul>
</div>
</div>
</nav>
User Workflow:
| Storage Key | Purpose | Managed By | Contains |
|---|---|---|---|
movieCreditsConfig |
Visual effects settings | customizeText3.html | Font, size, blur, stroke, colors, timing |
movieCreditsContent |
Text content | customizeContent1.html | Names, concepts, phrases, title |
âââââââââââââââââââââââââââââââââââââââââââ
â User opens customizeContent1.html â
ââââââââââââââââŦâââââââââââââââââââââââââââ
â
âŧ
âââââââââââââââââââââââââââââââââââââââââââ
â Auto-load from localStorage â
â Key: 'movieCreditsContent' â
ââââââââââââââââŦâââââââââââââââââââââââââââ
â
âŧ
âââââââââââââââââââââââââââââââââââââââââââ
â Populate text areas and inputs â
â - Student names (one per line) â
â - Technical concepts (one per line) â
â - Intro phrases (3 inputs) â
â - Movie title (with \n) â
ââââââââââââââââŦâââââââââââââââââââââââââââ
â
âŧ
âââââââââââââââââââââââââââââââââââââââââââ
â User edits content â
â Live count updates show totals â
ââââââââââââââââŦâââââââââââââââââââââââââââ
â
âŧ
âââââââââââââââââââââââââââââââââââââââââââ
â User clicks "Save Content" â
â Parse text areas to arrays â
â Store as JSON in localStorage â
ââââââââââââââââŦâââââââââââââââââââââââââââ
â
âŧ
âââââââââââââââââââââââââââââââââââââââââââ
â User opens movieCreditsSim15.html â
â Simulator loads both configs: â
â - movieCreditsConfig (effects) â
â - movieCreditsContent (text) â
ââââââââââââââââŦâââââââââââââââââââââââââââ
â
âŧ
âââââââââââââââââââââââââââââââââââââââââââ
â Apply custom content to constants: â
â - INTRO_PHRASE_1, _2, _3 â
â - MOVIE_TITLE_TEXT â
â - studentNames[] array â
â - technicalConcepts[] array â
ââââââââââââââââŦâââââââââââââââââââââââââââ
â
âŧ
âââââââââââââââââââââââââââââââââââââââââââ
â Generate credits from arrays â
â Each student/concept becomes a credit â
â object with position, color, etc. â
ââââââââââââââââŦâââââââââââââââââââââââââââ
â
âŧ
âââââââââââââââââââââââââââââââââââââââââââ
â Animation plays with custom content â
â Personalized intro â title â credits â
âââââââââââââââââââââââââââââââââââââââââââ
| Feature | Version 14 | Version 15 |
|---|---|---|
| Visual Effects | Customizable via customizeText3.html | â Same (unchanged) |
| Student Names | Hardcoded in JavaScript | â Editable via customizeContent1.html |
| Technical Concepts | Hardcoded in JavaScript | â Editable via customizeContent1.html |
| Intro Phrases | Hardcoded constants | â Editable via customizeContent1.html |
| Movie Title | Hardcoded constant | â Editable with multi-line support |
| Green Color Range | 100-140 hue (broad) | â 110-130 hue (refined) |
| Storage System | Single key: movieCreditsConfig | â Dual keys: config + content |
| Live Feedback | Preview in customizer | â Preview + count displays |
1. MVC Architecture Pattern
2. Data Persistence Strategies
3. String Processing
.split('\n')).trim()).filter()).join('\n'))\\n vs \n)4. User Experience Design
5. Functional Programming
.split().map().filter()6. Fallback Patterns
savedContent?.students || DEFAULT_STUDENTSsavedContent?.introPhrase1Version 15 delivers a complete content management system:
Content customization brings true personalization to the Movie Credits Simulator.