🎬 Building the Movie Credits Simulator
From a standalone Superman-style animation
to a fully integrated TNT ecosystem app with cinematic controls.
This log covers two development sessions. The first integrated
movieCreditsSim18.html — a Stage 16d Superman-style movie credits
animation — into the TNT site ecosystem: standard navbar, footer, SEO meta tags,
updated dependencies, and a dark-canvas layout with no hero.
The second session added a Reset button and redesigned all three control buttons as outline-style with semantic color coding, so they complement rather than compete with the animation canvas. This document will grow as additional stages are developed.
I am updating a ‘movie credits’ app (v Sim18 attached) to fit into our
TNT ecosystem. A previous app, computePi.html has the navbar and footer
I’d like for this app. Although I normally want a hero area for my apps, I don’t
want one on this app because I want the preview image/canvas/animation to be fully visible
when the page loads. Can you make these adjustments to the movieCreditsSim18 file and include
reasonable SEO information? At this time, we won’t use any of the links from the former
page, just ones we normally use in the TNT framework.
This prompt demonstrates three excellent practices simultaneously:
- Reference by example (“
computePi.htmlhas the navbar and footer I’d like”) — naming a concrete model eliminates ambiguity about layout, colors, and link structure - Explicit exception (“I normally want a hero area … I don’t want one on this app”) — calling out the deviation from pattern prevents the AI from defaulting to the standard template
- Link scope constraint (“we won’t use any of the links from the former page, just TNT framework links”) — prevents the old app-specific links (Content, Effects, About) from leaking into the new version
The combination of “use this as your model” + “except for this one thing” + “scope the links this way” gives the AI exactly the right amount of constraint to work efficiently without over-specifying every detail.
Examining movieCreditsSim18.html against the computePi.html
template, eight changes were required:
| Area | Before | After |
|---|---|---|
| Title & SEO | No og: tags, vague title | Full og: protocol + description + author |
| Favicon | images/favicon-32x32.png (broken relative path) | ../images/favicon-32x32.png |
| Bootstrap CSS | 5.3.0 | 5.3.3 |
| Additional dependencies | Missing Font Awesome, Boogaloo/Inter, tnt-base-styles | All three added |
| Custom navbar CSS | 25 lines: .navbar, .navbar-brand, .nav-link | Removed — tnt-base-styles.css provides all of these |
| Canvas container | margin-top:0; min-height:calc(100vh - 70px) | min-height:calc(100vh - 68px) for sticky navbar |
| Navbar HTML | Custom purple gradient nav, app-specific links | TNT standard #mainNav, Processing active |
| Footer | None | TNT standard footer + IIFE + linkGuard.js |
Every other TNT app opens with a hero section — a full-width banner with a background image, an eyebrow label, and a title. The Movie Credits Simulator is the first to forgo it deliberately.
The reason is visual hierarchy: the animation is the first experience. A hero above the canvas would push the 800×600 starfield below the fold on most screens, forcing the user to scroll before anything moves. Since the loading state (spinner) transitions directly into the starfield animation, the canvas is the hero — there is nothing to add above it.
Implementation: #canvas-container is given
min-height: calc(100vh - 68px) so the canvas area fills the full viewport below the
sticky navbar on first load.
The original file had a self-contained purple gradient navbar defined entirely
in its own <style> block. When tnt-base-styles.css is loaded,
it provides its own complete rules for #mainNav, .navbar-brand, and
.nav-link.
Leaving the custom rules in place would create a silent specificity conflict — the cascade would resolve it differently depending on selector specificity and source order, producing unpredictable results across browsers.
The correct action is removal of the custom rules, not adding override selectors on top of them. Integration means subtraction as well as addition.
The integration added the Open Graph protocol tags
(og:type, og:title, og:description,
og:url, og:image) rather than just
<meta name="description">.
The distinction matters for discoverability: search engines use
og: tags to generate rich link previews. When a student shares the page URL,
the platform shows the app name, description, and an image rather than a bare link.
The og:image was set to the TNT dynamite SVG as a deliberate
placeholder. A production deployment would replace it with a screenshot of the animation
in action — the starfield with a credit name zooming through it is immediately
recognisable and more compelling than a site icon.
In our app, let’s provide a ‘Reset’ button that takes the app back to the state where it begins when the page launches. Given the fact we’ll now have three control buttons, suggest a visually meaningful color scheme; I’m thinking outline buttons might even be a better choice, so as not to compete with the artwork; they could be ‘solid’ on hover.
This prompt is notable for what it suggests rather than mandates:
- Behavior first (“takes the app back to the state where it begins when the page launches”) — defines semantic intent before naming the control, making the requirement implementation-independent
- Design rationale included (“so as not to compete with the artwork”) — gives the AI the why so it can make correct subordinate decisions (transparent at rest; solid only on hover)
- Invitation to suggest (“suggest a visually meaningful color scheme”) — explicitly invites a design recommendation rather than dictating hex values
The phrase “visually meaningful” is the operative instruction. It signals that colors should carry semantic weight — not just look attractive — which led directly to the primary/secondary/utility hierarchy below.
The resetAnimation() function was already complete — it stops music,
restores volume, resets all timing flags, reinitialises the intro phrases, title, and
credits, and returns START/PAUSE to its initial label. Exposing it required three steps:
- Add
<button id="resetBtn">RESET</button>between START and MUTE - Wire
resetBtn.addEventListener('click', () => resetAnimation())insetupButtons() - Redesign all three buttons as a coordinated outline set
For the color scheme, three semantic roles map to three distinct colors:
| Button | Color | Hex | Semantic meaning |
|---|---|---|---|
| START / PAUSE | Blue-purple | #667eea |
Primary action — consistent with the loading spinner; “play” is the app’s core purpose |
| RESET | Amber | #f0a500 |
Secondary action — warm “restart” energy; avoids the alarm of red, which implies danger or permanent deletion |
| MUTE | White/silver | rgba(255,255,255,0.5) |
Utility — quietest visual presence; shifts to red (#ff6b6b) when muted to signal active suppression |
All three buttons are transparent with a colored border at rest. On hover, they fill solidly with their respective color. This directly serves the “don’t compete with the artwork” requirement:
- At rest, three hairline-bordered labels float above the starfield with minimal visual weight — the stars and credit names are never obscured
- On hover, the solid fill signals interactivity without the button dominating the scene when idle
backdrop-filter: blur(6px)gives the buttons a subtle frosted-glass effect, integrating them with the dark canvas rather than sitting on top of it as opaque elements
The amber hover fill for RESET uses color: #111 (near-black text). Amber at full saturation does not provide sufficient contrast against white text — dark text on amber is the standard accessible combination for this hue.
When audio is muted, the MUTE button border and text shift from white to red
(#ff6b6b). This is not merely decorative — it makes the muted state
persistently visible without requiring the user to hover or test it.
The pattern follows familiar UX conventions: a recording indicator shows red when active, a crossed microphone icon appears red when suppressed. Red on the MUTE button reads immediately as “audio is off right now.”
Users who return to the tab after muting (or who are handed a device mid-session) will see the red border and understand the state at a glance, without needing to click anything to discover it.
The R-key reset shortcut existed since Stage 9 for developer convenience during testing. It was never documented in the UI. Exposing it as a visible RESET button required no new logic — only a DOM element, one event listener line, and a CSS rule.
Invisible functionality is inaccessible to anyone who doesn’t read source code. Any keyboard shortcut that performs a user-facing action should have a visible counterpart. The shortcut can remain for power users; the button serves everyone else.
- Integration means subtraction as well as addition.
Loading a shared stylesheet like
tnt-base-styles.cssis not purely additive if conflicting rules remain in the page. Custom navbar CSS that worked standalone becomes a specificity conflict in a framework context. Correct integration requires identifying and removing the rules the framework replaces — not adding override selectors on top of them. - The canvas can be the hero. Animation-first apps do not need a traditional hero section. When the page’s primary value is a visual experience that begins on load, a hero above the canvas is structural overhead that delays the first impression. The loading-to-animation transition is itself the moment of arrival. Make the canvas fill the viewport and let it speak.
- Semantic color beats aesthetic color. Three buttons of the same style with arbitrary colors are decorative. Three buttons where each color carries a meaning — primary / secondary / utility — build a visual grammar that users read without explanation. The grammar here is simple: blue starts things, amber resets things, white manages audio. Students can read the interface before they read the labels.
- Expose keyboard shortcuts as visible controls. Hidden keyboard shortcuts are developer conveniences, not user features. Any shortcut that performs a meaningful user action should have a visible button. Buttons are accessible to touchscreen users, to users who don’t know the shortcuts, and to users encountering the app for the first time. The shortcut key can remain for power users; the button serves everyone else.
- Reference by example, constrain by exception. Both prompts in this session followed the same efficient pattern: name a concrete existing artifact as the baseline, then name the explicit deviation from it. This produces more precise results than describing every desired attribute from scratch — and more natural ones, because the AI works from a proven, tested model rather than an abstract specification.
| Change | Status | Description |
|---|---|---|
| SEO meta tags | ✓ Done | Title, description, author, og:type/title/description/url/image |
| Favicon path | ✓ Done | Fixed images/ → ../images/ |
| Bootstrap 5.3.3 | ✓ Done | CSS and JS bundle upgraded from 5.3.0 |
| Font Awesome 6.5 | ✓ Done | Added for footer icon compatibility |
| Google Fonts | ✓ Done | Boogaloo + Inter added alongside existing cinema fonts in one request |
| tnt-base-styles.css | ✓ Done | Added; conflicting custom navbar CSS removed |
| TNT navbar | ✓ Done | Standard #mainNav, Processing link marked active |
| TNT footer | ✓ Done | Icon row, copyright/date spans, IIFE, linkGuard.js |
| No-hero layout | ✓ Done | Canvas fills viewport below navbar; min-height: calc(100vh - 68px) |
| Reset button | ✓ Done | Exposed existing resetAnimation() as a visible control |
| Outline button design | ✓ Done | Transparent at rest, solid fill on hover; semantic color hierarchy |
| Muted state indicator | ✓ Done | MUTE button border shifts to red when audio is suppressed |
| Hero background image | ✓ Done | Restored movieCreditsCoverHeroImg.jpg; og:image updated to match |
| Page background | ✓ Done | Darkened body + content boxes for smoother hero–content transition |
| Body background (Session 4) | ✓ Done | Shifted to #57b9d6 — teal-blue echoing the hero warp-tunnel palette |
| Log-divider color (Session 4) | ✓ Done | Darkened from #aaa to #666565 for legibility on new background |
Backup movieCreditsSim18a.html | ✓ Done | Stable copy preserved before structural changes |
| Canvas height | ✓ Done | Reduced 600 → 500 px so canvas + controls + footer fit one screen |
| Controls moved below canvas | ✓ Done | #control-bar in document flow; no more absolute-position overlap |
| Timeline slider | ✓ Done | Drag to jump to any credit phase; auto-advances during playback |
| Music synchronisation | ✓ Done | Slider seek now starts music at proportional position via audio.currentTime |
movieCreditsSimCover.html | ✓ Done | Cover / index page with three launch cards; sub-navigation bar added |
| Simulator “Movie Credits Home” link | ✓ Done | Updated from href="#" to movieCreditsSimCover.html |
customizeContent18.html | ✓ Done | Content customizer: names, concepts, phrases & title — no code changes needed |
| Cover page updated | ✓ Done | 4th card (Content/teal), spark-bar link, “Coming soon” → “Now available” |
customizeText18.html | ✓ Done | Settings customizer with live p5.js preview; 16 controls; saves to movieCreditsConfig |
| Cover page (Session 9) | ✓ Done | 5th card (Settings/green); broken nested-row fixed; spark-bar + heading updated |
| Heading validation fix | ✓ Done | customizeText18: <h3> → <h2 class="panel-title"> — heading-level skip resolved |
| Heading styling restored | ✓ Done | customizeContent18: .panel-title class restores Boogaloo look on the semantic <h2> |
| Settings reset modal | ✓ Done | Styled Bootstrap modal replaces confirm() in customizeText18.html |
| Content reset modal | ✓ Done | Same modal pattern applied to customizeContent18.html with content-specific warning list |
| Sub-nav bar (chatlog) | ✓ Done | Replaced .back-bar with .mc-spark-bar — all six project pages now share the same sub-navigation |
| Bottom link bar contrast | ✓ Done | Home button deepened #f0a500 → #8b4800; ratio 1.1:1 → 3.1:1 (C.R.A.P. Contrast principle) |
| TNT site integration | ✓ Done | Added to news.html (entry #041) and explore.html S.P.A.R.K., Processing, and Simulations offcanvas panels |
I inadvertently removed the background image I had requested for the hero area. Please re-insert and then adjust the main page background and found colors with a slightly darker color so the difference between the hero and page colors is not so stark. Please write up these adjustments in the log as well.
The hero background image (movieCreditsCoverHeroImg.jpg) was
restored after being accidentally removed in a manual edit between sessions. The image
shows a blue warp-tunnel / hyperspace effect — visually consistent with the
Superman-style starfield animation it represents on the chatlog page.
The overlay gradient was kept deliberately light
(rgba(5,8,22,0.72) → rgba(10,20,50,0.55)) so the cinematic
blue reads clearly through it. A heavier overlay would defeat the purpose of having the
photograph.
The og:image meta tag was also updated from the generic TNT
icon to the cover photograph, so social link previews now show the warp-tunnel image
rather than a site logo.
The original body background (#f0f2f8) sat immediately after
the near-black hero, creating a hard light-to-dark boundary. Darkening all content
surfaces proportionally moves the whole page toward the hero’s tonal range without
making the content area uncomfortable to read:
| Element | Before | After |
|---|---|---|
| Page body | #f0f2f8 | #e2e6f0 |
| Aside boxes | #f8f9fa | #edf0f7 |
| Decision boxes | #fffbf0 | #f4eedf |
| Critique boxes | #f0f4ff | #e6ebf8 |
Each box retains its original color temperature — aside boxes stay cool grey, decision boxes stay warm amber-tinted, critique boxes stay cool purple-tinted. Only the lightness value shifts, preserving the visual grammar that distinguishes each box type.
I made minor color adjustments in the background color and some font colors for the log-divider. Can you adjust the necessary details in the chatlog?
The body background was changed from #e2e6f0 (muted
blue-grey) to #57b9d6 (vibrant teal-blue). Where the previous value
simply reduced the lightness of the original #f0f2f8, this shift pulls
the page color into the same teal family as the warp-tunnel hero photograph —
creating tonal continuity from the dark navy hero through the bright teal content
area rather than a neutral-to-dark transition.
The content boxes (#edf0f7, #f4eedf,
#e6ebf8) now sit on a warmer, more saturated background, which makes
their individual color temperatures more visually distinct by contrast.
The log-divider text color was darkened from #aaa
(light grey) to #666565 (mid grey). On the previous
#e2e6f0 background the divider had workable contrast; on the more
saturated #57b9d6 background a lighter grey risks washing out against
the vivid blue.
The flanking rule lines (::before / ::after)
remain at #ddd — they are structural separators rather than
readable text, and their slight translucency against the blue reads as intentionally
subtle.
Looking at the simulation, it appears as if the length of the animation is based on the number of elements that fly into the screen. Once there are no more elements, the image just shows the stars moving from the center to the edge of the canvas, as desired. I’m thinking as an additional control, that we could add a slider so we can move to whatever phase of the credits we want. Please confirm my hypothesis and if I’m right, can we add the slider to the controls (above the buttons) and then adjust the layout in such a way that the canvas and all controls show on the screen (without vertical scrolling) and without the controls overlapping the canvas area? If this is complex enough that we might ‘break’ something, let’s make a copy of version 18 as version 18a.
Hypothesis confirmed. The animation duration is directly proportional to the number of credits. With default content (5 students + 15 concepts = 20 credits), the timeline breaks down as follows:
| Phase | Duration | Source |
|---|---|---|
| Intro (4 phrases) | ~13.1 s | 500 + (3000×4) + 250 + 250 + 100 ms |
| Credits pause | 11.5 s | CREDITS_PAUSE = 11500 |
| Per credit (×20) | ~8.9 s each | ~3.9 s animate + 5 s delay |
| Total (default) | ~204 s ≈ 3.4 min | Scales linearly: +8.9 s per additional credit |
Structural complexity is high enough to warrant a backup.
movieCreditsSim18a.html was copied before any changes were made.
The canvas was reduced from 800×600 to 800×500 so that canvas + control bar + footer fit within a typical 768–900 px laptop viewport without vertical scrolling:
| Element | Height |
|---|---|
| Navbar | 68 px |
| Canvas (new) | 500 px |
| Control bar | ~90 px |
| Footer | ~80 px |
| Total | ~738 px |
All p5.js code that references the height variable
(star spawning, credit/title vertical centering) adapts automatically because
height is a p5.js live variable, not a hard-coded constant.
The original #controls div was position:
absolute at the bottom of #canvas-container, which placed
the buttons visually over the p5.js canvas. This overlap is fine when the canvas
is large enough, but it becomes a problem when adding a second control row (the
slider) — both rows would need to float above the artwork.
Moving the controls to a new #control-bar div in
normal document flow (below #canvas-container) eliminates the overlap
entirely. The control bar has its own background (rgba(0,0,0,0.55)),
creating a clear visual separation between the animation canvas above and the
interactive controls below — similar to a video player layout.
The timeline slider serves two roles:
- Scrubber: dragging and releasing calls
seekToCredit(k), which jumps the animation to credit k instantly - Live indicator:
activateCredit(index)now updatesslider.value = index + 1every time a credit activates, so the thumb advances automatically during normal playback
Position 0 = “INTRO” (full sequence from the beginning, music included). Positions 1–N jump directly to that credit, suppressing the intro. The amber label to the left of the slider shows the credit name at the current thumb position as the user drags, giving a preview before committing.
Infinity Start TimesWhen seekToCredit(k > 0) is called, the four intro
phrase objects have their startTime property set to
Infinity. The activation check in updateTitleObject() is:
if (!titleObj.active && timeSinceStart >= titleObj.startTime)
Since no finite elapsed time can equal or exceed Infinity,
the condition is permanently false. The phrases are never activated — they sit
inert while the credits sequence runs normally from the target position.
The virtual clock is positioned by setting
animationStartTime = millis() - creditsStartTime - 10, so
timeSinceStart = creditsStartTime + 10 from the very first frame —
credits start immediately without waiting through the intro or pause.
As a ‘novice’ my hunch is that it’s too complicated to match the location of the music track to the location of the slider. We DO have the length of the music so if there is a way to begin music at a particular location, I’d say we can synchronize the music with the position of the slider. Before making any changes please critique my hunch. Is this task impossible? Too complicated? A piece of cake?
This is an excellent use of AI: ask first, code second. When a task feels complicated, the fastest path to a good outcome is to ask the AI to evaluate the difficulty before writing any code. If it’s harder than expected, you learn that before making a mess. If it’s easier, you gain confidence and understanding before you see the solution.
The phrase “as a ‘novice’” is also a powerful framing signal — it tells the AI that the answer should include an explanation, not just a verdict.
A piece of cake. HTML5 Audio exposes a property called
currentTime that stores how many seconds into the track the
playhead is sitting. You can read it to find out where the music
is, and you can write it to jump anywhere you want — exactly
like dragging the thumb on a music app’s progress bar. Setting it is
one line:
supermanMusic.currentTime = 45.3; // jump to 45.3 seconds in
The math is equally simple. Think of two rulers side by side. One ruler represents the full animation duration (~3.4 minutes with default credits). The other represents the music track (fixed length). To find the right spot on the music ruler, just find the same percentage mark:
fraction = estimatedAnimTime / totalAnimDuration; audio.currentTime = fraction * audio.duration;
The result is “proportional sync” — jump to credit 10 out of 20 and the music starts at roughly the 50% mark of the track. It won’t land on a specific musical beat, but it will feel right because the energy of the music matches the energy of the scene.
Total new code inside seekToCredit(): 6 lines.
audio.currentTimeHTML5 Audio is not a black box. It exposes its internal state
through readable and writable properties. The most important one here is
currentTime:
| Property / action | What it means | Example value |
|---|---|---|
audio.duration | Total length of the track (seconds) — read-only | 184.3 |
audio.currentTime (read) | Where the playhead is right now (seconds) | 72.1 |
audio.currentTime = n (write) | Jump to n seconds — like dragging a slider | supermanMusic.currentTime = 92.2; |
Setting currentTime before calling .play()
is exactly what every music player, podcast app, and video website does when you
drag the progress bar. It is a standard, well-supported feature of the web
platform — not a workaround or a trick.
Imagine laying two rulers side by side. One ruler represents the total animation (its length varies based on the number of credits). The other represents the music track (its length is always the same). To synchronise them, find the same percentage mark on both:
| Step | In plain English | In code |
|---|---|---|
| 1 | Estimate where in the animation credit k appears | t = creditsStart + k × (animDuration + delay) |
| 2 | Divide by total animation length → a fraction from 0.0 to 1.0 | f = t / totalDuration |
| 3 | Apply that same fraction to the music length | audio.currentTime = f × audio.duration |
The credit animation duration used in step 1 is computed
from constants already in the file — how fast the credit shrinks
(CREDIT_SHRINK_RATE) divided by 60 frames per second, converted to
milliseconds. No new magic numbers needed.
What “proportional sync” sounds like in practice: if you jump to the last credit in a long show, the music is near its climax. Jump to the first credit and the music is just warming up. The emotional arc of the track matches the visual arc of the sequence — which is exactly what you want from a credits roll.
A previous app for Conway’s Game of Life featured a ‘cover’
type page. We’d like a similar page, movieCreditsSimCover.html,
that hosts a card to Run the simulation, View the chatlog for this version,
and to run a ‘legacy’ version that’s also in the folder:
version 16dFix3. Can you create such an ‘index’ and link it to
the chatlog and to the link next to ‘Tech Novice Tools’ on the
simulator page? With this success, we’ll then work on an update to the
Settings and Content adjustments for the app and with those done, we’ll
update the index cover page and then be ready to wire this all up into TNT
and make it live!
This prompt combines two powerful techniques in a single message:
- Reference by example (“A previous app for Conway’s Game of Life featured a cover page”) — names a specific existing model so the AI knows the exact format without further description
- Explicit roadmap (“With this success, we’ll then work on Settings and Content … then wire into TNT”) — stages the work visibly, letting the AI document the plan in the chatlog even before those stages begin
Naming future work in the current prompt is an underused technique. It gives the AI enough context to write a “Coming soon” callout on the cover page and set expectations for the next sessions without requiring a separate planning conversation.
The cover page (movieCreditsSimCover.html) is modelled on
gLifeIndex.html from the Conway’s Game of Life project: a dark-themed
entry page with three launch cards, a hero banner, and a brief “what students
learn” callout. The dark theme (#0d1117 body) was chosen to match
the animation’s own dark canvas rather than the chatlog’s teal palette.
The three cards follow the same semantic color hierarchy as the simulation’s buttons: blue-purple for the primary action (Run), amber for the development story (Chat Log), and silver for the archival reference (Legacy v16d).
The “Coming soon” paragraph in the callout serves a dual purpose: it informs the current visitor and creates a public commitment to the next development stages.
A .mc-spark-bar strip (modelled on the GOL
.spark-bar) sits below the TNT navbar on the cover page and provides
quick links between all project pages:
Movie Credits | Simulation | S.P.A.R.K. Chat Log | Legacy v16d
The bar is on the cover page for now. As the Settings and Content pages are added, they will be included here too — giving every project page one-click access to every other page without going back to the TNT main navbar.
The session established a clear three-stage plan before full TNT integration:
| Stage | Page(s) | What it delivers |
|---|---|---|
| Next | movieCreditsSettings.html | Typography (font, size, tilt), blur, timing controls — currently hard-coded constants in the JS |
| Then | movieCreditsContent.html | Student names, CS concepts, intro phrases, movie title — currently set in the DEFAULT arrays |
| Then | Update movieCreditsSimCover.html | Add Settings and Content cards; sub-nav bar updated to include both pages |
| Then | TNT integration | Wire into processing_apps.html, explore.html, news.html |
The legacy version already has working equivalents of these pages
(customizeText3.html for settings, customizeContent1.html
for content). They provide a useful starting reference for the new versions.
A previous version of this app allowed for content customization (attached).
Let’s use this as a basis for creating customizeContent18.html
where users can edit the movie content as done previously. It should fit into
the current scheme and provide instructions. It should link to the cover page
and let’s write it all up in the chatlog.
“Use this as a basis” is the operative instruction. Rather than describing every desired feature from scratch, the user points to a working reference and asks for an adaptation. This produces better results because:
- The JS logic is already proven — the localStorage read/write, validation, and reset function work correctly in the legacy version and only need the same keys and defaults to work identically with Sim 18.
- The scope is clear (“fit into the current scheme” + “provide instructions” + “link to the cover page”) — three additions to the reference, no more.
The customizeContent1.html (legacy) and customizeContent18.html
(new) share the same localStorage key (movieCreditsContent), the same JSON
structure, and identical validation rules. The JS logic was kept functionally identical.
The presentation layer was completely replaced: custom
purple gradient navbar → TNT #mainNav; Bootstrap 5.3.0 → 5.3.3;
Font Awesome added; inline style block replaced with named .cc-* classes
on a dark (#0f1219) background. The spark-bar (with Content marked active)
gives the page its place in the project navigation.
The simulator reads content under the key movieCreditsContent.
The customizer writes to exactly the same key with the same JSON shape
(introPhrase1/2/3, movieTitle, students,
concepts). No changes to the simulator were needed.
The DEFAULT_* constants in the customizer match the
simulator’s own defaults exactly, so a teacher who resets gets the same starting
point they would see on a fresh simulator load — factory names and concepts
appear identically in both files.
The legacy customizer required the user to navigate back to the
simulator manually. The new version adds a Test in Simulator button
(amber outline, matching the RESET button color in the simulator) that links directly
to movieCreditsSim18.html immediately after saving.
This creates a tight workflow: edit → save → click → see results. The button is an amber outline — not a filled call-to-action — because it is a navigation link rather than a data action, and because amber already carries the meaning “secondary action” in this project’s visual grammar.
A legacy version of our movie credits app allowed us to customize the
text in the credits. It’s attached as customizeText3.html.
We made a copy, customizeText18.html that we’d like to
implement in this upgraded version (18). Can you create a similar document
as customizeContent18.html and then ‘wire it up’
in the cover page as a 3rd card alerting users that they have this feature?
“Create a similar document as customizeContent18.html”
is the most efficient instruction possible here. By naming an already-completed,
working page as the template, the user communicates the entire presentation layer
(dark theme, TNT navbar, spark-bar, compact hero, instructions panel, button styles)
in a single phrase.
The phrase “wire it up in the cover page” signals that both the HTML and the cover-page navigation need updating — without the user having to enumerate every affected file.
The key difference between customizeContent18.html and
customizeText18.html is the live p5.js canvas on the Settings page.
Content changes (names, phrases, title) are text — the user can mentally
preview them. Visual settings (font, blur, tilt, stroke) have effects that are
hard to imagine without seeing them. The canvas preview makes every slider
change immediately tangible.
The canvas is 700×500 and sits in a col-lg-7
column, leaving col-lg-5 for the scrollable controls panel
(max-height: calc(100vh - 180px)). This classic split-panel layout
is standard for any tool that has both settings and a visual output.
The legacy customizer had two defaults that did not match
movieCreditsSim18.html:
| Property | Legacy default | Sim18 default | Corrected to |
|---|---|---|---|
fontFamily | Bebas Neue | Righteous | Righteous |
creditDelay | 1500 ms | 5000 ms | 5000 ms |
If a teacher uses the Settings page without changing these values and then clicks “Save to Browser”, the simulator will load exactly the defaults it would use without any saved config — no surprise behaviour.
Adding the Settings card gave the cover page five cards. The user had
also introduced a structural HTML error while manually rearranging cards: the Content
card was inside a nested <row> inside the main card row,
which broke the Bootstrap grid. Both issues were fixed in a single pass:
- Row 1 (3 cards): Simulation, Content, Settings — the active tools
- Row 2 (2 cards, centered): Chat Log, Legacy v16d — reference and archive
The Settings card uses green (#66bb6a) — the
fourth distinct color in the project’s card palette, after blue-purple
(action), amber (navigation), and teal (content). Green suggests
“configuration” or “fine-tuning” — a common
convention in interface design.
.panel-title FixIn the customizeText page, the document fails to validate because of the h3 heading, but I like the look of it; in the customizeContent page, I fixed the validation problem, but the look is ‘off’ of the heading in the control panel. Can you make a style and apply it to both headings so they look like they do in the text doc in each (for consistency) but both still validate? This is important to write up in the chatlog. I’m interested in why you think this is important to me.
Three patterns in this project make heading structure particularly important:
- Every TNT footer has a “Validate this page” link.
It’s wired into
linkGuard.jsand appears on every page in the ecosystem. Running pages that produce validation warnings while prominently linking to a validator is a contradiction — and you are meticulous about consistency. - This is a teaching tool. The chatlog documents every
decision for students. A heading skip silently teaches bad HTML. The fix is
itself a lesson: CSS classes separate what something is (an
h2) from how it looks (Boogaloo, 1.25rem, amber icon). That principle is fundamental to maintainable front-end code. - Accessibility. Screen reader users navigate pages by heading
structure. A skip from
h1toh3creates a disorienting gap. For a site used in classrooms that may include students with disabilities, this matters.
In short: you noticed the inconsistency and asked for it to be fixed in the chatlog — which means you want students to see the reasoning, not just the outcome.
HTML requires headings to descend in order:
h1 → h2 → h3.
Skipping a level (e.g., h1 → h3) is valid HTML5 syntax
but produces a WCAG accessibility warning because screen reader users use heading
structure as a navigation map. A missing h2 is like a table of contents
with no chapter titles — you jump from the book title straight to sub-sections.
The Settings page had exactly this: a hero h1 followed
by an h3 in the controls panel, with nothing at h2 level.
The Content page had the inverse problem: the user correctly changed h3
to h2 for validation, but the CSS rule .cc-panel h3 no
longer matched — leaving the heading unstyled (browser default bold serif).
.panel-title: Style the Class, Not the ElementThe fix is a CSS class (.panel-title) that carries the
visual style independently of the element type:
.panel-title { font-family:'Boogaloo'; font-size:1.25rem; color:#fff; margin-bottom:1rem; }
This means:
- The element (
h2orh3) communicates hierarchy to the browser, screen readers, and search engines - The class communicates appearance to the CSS engine
- Changing one does not break the other
The same class is defined in both files with identical values,
so a teacher who opens either customizer page sees the same panel heading style.
The shared name (.panel-title) signals their intended equivalence
even though the two files are independent.
The reset button appropriately launches a popup ‘warning’ but its ugly, plain alert-type packaging. Please create a styled modal that looks more professional to alert the user about a factory-default reset. Please include this update in the chatlog with a compare-contrast of modal popups with ‘alert’ type popups, and also include why its professional to give the user a ‘speed-bump’ before doing something that can’t be undone.
confirm() vs Bootstrap Modal: A Comparisonalert() and confirm() are browser-native
functions inherited from the early web. They work, but they come with significant
limitations that become visible on a professional-quality tool:
| Feature | confirm() | Bootstrap Modal |
|---|---|---|
| Appearance | OS-native — cannot be themed or styled | Fully customizable: dark theme, icons, Boogaloo heading |
| Content | Plain text only | Full HTML: headings, lists, color-coded warnings |
| JavaScript | Blocks all JS execution while open | Non-blocking; event-driven click handlers |
| Button labels | “OK” / “Cancel” (browser-fixed) | Any text, any icon, any style |
| Accessibility | Minimal ARIA support | Full: aria-modal, aria-labelledby, focus trap |
| Mobile behaviour | Varies widely across browsers | Consistent Bootstrap rendering |
| App integration | Jarring visual context switch | Seamless in-app — backdrop preserves the page behind it |
The modal also uses the same .panel-title class established
in Session 10, so the “Reset to Factory Defaults?” heading
matches the rest of the UI without any additional CSS.
A speed bump is a deliberate UX friction point inserted before any action that cannot be reversed. Skipping it feels faster for the developer but creates a worse experience for the user — and worse outcomes when a mistake is made.
Three things make a good speed bump for a destructive action:
- Specify what will be lost. “Are you sure?” is not informative. “Your student names, CS concepts, and intro phrases will be permanently erased” is. The user can make a real decision only when they know the stakes.
- Make the safe path the easy path. Cancel is on the left — the natural first target. The destructive action is on the right, requiring a deliberate rightward movement and click.
- Name the consequence explicitly. The amber line “This cannot be undone” is not decoration. It is the single most important sentence in the dialog: it tells the user they are about to cross a one-way door.
The data-bs-dismiss="modal" attribute on the confirm
button lets Bootstrap handle the close animation cleanly, while the
click event listener on the same button handles the actual reset.
Both fire independently — no manual modal.hide() needed.
When this project had three pages, a simple “Back” link was sufficient. By Session 11 it has six: the cover, simulator, content customizer, settings customizer, chatlog, and legacy reference. At that scale a single back-link fails for two reasons:
- It only navigates one direction — you can reach the cover page from the chatlog, but not the settings page, or the simulator, without going via the cover
- Each page has a different version of “back”, so users on different pages experience different navigation
The .mc-spark-bar appears identically on all six pages, with only the class="active" attribute changing to indicate where the user is. This one-line difference per page costs nothing and gives every visitor a mental map of the whole project from any page they land on.
The .back-bar CSS was also removed from the chatlog’s <style> block — dead CSS is clutter that can confuse maintainers later. The rule for removing unused code is the same as the rule for adding new code: if it serves no purpose, it should not be there.
At the bottom of the page, the link to the Home is a gold color that’s slightly difficult to read against the blue background. Please darken that gold color, tell me how you did it and let’s write this up in the chatlog, reminding everyone about the ‘C.R.A.P’ rule of design: Contrast, Repetition, Alignment and Proximity: specifically — Contrast — being able to clearly read elements on a page.
C.R.A.P. is a four-principle visual design framework popularised by Robin Williams in The Non-Designer’s Design Book. Each letter names a property of well-organised visual layouts:
| Principle | What it means | Example in this project |
|---|---|---|
| Contrast | If two elements are different, make them very different — never almost-the-same | Dark amber text on teal background instead of near-identical-brightness amber on teal |
| Repetition | Repeat visual elements across pages to build a consistent visual identity | The .mc-spark-bar appears identically on all six project pages; the same button styles recur on every customizer |
| Alignment | Every element should have a visual connection to something else — nothing placed arbitrarily | Bootstrap’s grid aligns cards in rows; button groups use d-flex gap-2 for even spacing |
| Proximity | Group related items together; physically separate unrelated ones | Save / Reload / Reset cluster together; the Test button sits apart because it navigates rather than saves |
The specific failure here — Contrast.
The button used the project’s standard amber (#f0a500) for its
text and border. That color has a relative luminance of approximately 0.454.
The page background (#57b9d6 teal) has a luminance of approximately 0.416.
The WCAG contrast ratio formula is (Llighter + 0.05) / (Ldarker + 0.05):
(0.454 + 0.05) / (0.416 + 0.05) = 0.504 / 0.466 = 1.08:1
A ratio of 1.08:1 is effectively no contrast at all —
both surfaces have almost identical brightness. The amber was not wrong in isolation;
it reads perfectly on the simulator’s near-black canvas and on the cover
page’s dark #0d1117 body. It fails only here because
the chatlog’s bright teal background happens to sit at the same
luminance level as the amber itself. This is the key lesson of the Contrast
principle: contrast is relational — it depends on
foreground and background together, not on either color alone.
The fix. The button color was changed from
#f0a500 to #8b4800 — a dark burnt amber.
Its luminance is approximately 0.101:
(0.416 + 0.05) / (0.101 + 0.05) = 0.466 / 0.151 = 3.09:1
WCAG 2.1 Level AA requires a minimum of 3.0:1 for large or bold
text. The button is rendered fw-bold (Bootstrap 700-weight), which qualifies.
3.09:1 clears that threshold — the label is now readable without guessing.
Practical rule: whenever a text or border color is chosen for a specific background, check that their luminances are not similar. If they are close, one must change. Make the foreground distinctly darker or distinctly lighter than the background — never almost-the-same. The WCAG contrast checker at webaim.org/resources/contrastchecker lets you paste two hex values and see the ratio instantly.
Before we launch the app live, we want a news entry announcing its inclusion (remember to use text, not button links at the bottom of the entry). We also want it featured in the explore page as a SPARK app, as well as a Simulation and Processing app. Remember, our goal is to only have 3 apps on the actual category cards: others are shown in the off-canvas link panels. Remember too that only the SPARK off-canvas panels link to chatlogs: other categories only feature the apps themselves. Of course, update our chatlog. This will likely be our last entry unless problems materialize. Be sure to update any lastUpdate date-stamps too on modified pages.
A TNT app is not “launched” simply because its files exist in the server folder. For a new app to be discoverable and documented, three files must be updated:
| File | What it provides | This project |
|---|---|---|
news.html |
Public announcement — what the app is, what it teaches, where to find it; text links only at the bottom, no styled buttons | Entry #041, Aug 2026: five paragraphs covering the animation engine, control system, five-page ecosystem, and chat log |
explore.html |
Discoverability by category — the app appears in every relevant offcanvas panel | Added to S.P.A.R.K. offcanvas (app + chatlog), Processing offcanvas (app only), Simulations offcanvas (app only) |
| This chatlog | Development record — every decision documented for learners | Sessions 1–13, Decisions 1–32, from ecosystem integration through C.R.A.P. contrast and launch |
Two rules govern explore.html entries that are worth naming explicitly:
- Category cards are capped at 3 featured apps. When a card already shows 3 apps, new apps go only into the offcanvas panel. The S.P.A.R.K., Processing, and Simulations cards were all at or over their limits, so Movie Credits appears in the offcanvas panels only — not on the cards.
- Only the S.P.A.R.K. offcanvas links to chatlogs. Processing and Simulations panels list apps only. Chatlog links belong in S.P.A.R.K., where the development dialog is the primary subject — not in subject-matter categories where visitors are looking for apps to run.
Thirteen sessions. Thirty-two decisions. One page for the canvas,
one for the cover, two for the customizers, one for the legacy, and one for the log.
Everything connected by shared localStorage keys, a consistent
sub-navigation bar, and a color grammar that starts with the simulator’s
three outline buttons and flows outward to every card, offcanvas entry, and news
headline in the ecosystem. The Movie Credits Simulator is live.