Parabola Saga Apps S.P.A.R.K. Development Log  •  All Phases  •  09/10/2026
S.P.A.R.K. with AI — Development Dialog

Parabola Saga — S.P.A.R.K. Development Log

All development phases — from grid infrastructure to the b-coefficient family.
Companion to “If I Only Had a Vertex” — the axis-shifter no verse named.

About This Log

This log chronicles the development of Parabola Saga — a SketchWaveJS app that lets students visually explore how the b coefficient in y = ax² + bx + c shifts the vertex of a parabola. The educational anchor is the TNT parody “If I Only Had a Vertex”, whose postscript Bonus Verse notes that b is the axis-shifter no verse named: h = −b / (2a).

Each phase is documented in its own section: Phase 1 (grid infrastructure), Phase 2 (single parabola with vertex), Phase 3 (multiple parabolas, upcoming). Each section records the development conversation, analyzes prompt strategy using S.P.A.R.K., and documents design decisions and bugs found during review. Watch for Prompt Critique boxes (amber) and Design Decision boxes (green).

The Goal
🎯
S — Set Goal Build a visual exploration tool showing how varying b (while keeping a and c fixed) shifts the vertex of a parabola horizontally. Students set a range bmin → bmax with increment binc and see the full family of resulting parabolas graphed on a shared coordinate system. Phase 1 scope: an adjustable Cartesian grid with centered origin, controllable half-size (±N), and controllable step size. Phase 2 adds the parabola drawing.
The Prompt
💬
P — Prompt The first prompt, sent with parabola1.html, onlyHadABrain2026.html, and analyzingOnlyHadAVertexLyrics.html attached as session context. The critique below identifies four strengths and five areas to improve for future sessions.
klp (TTG)

parabola1.html is a copy of a previous app that we want to refactor. If you look at our onlyHadABrain page and the associated analysis of the song parody, we want an app that will allow us to graph multiple parabolas of the form ax^2 + bx + c where we set the values of a and c and then let b values span a range from bmin to bmax with increment binc and graph the parabolas to study the effect that ‘b’ has on a parabola.

To this end, we want to start by controling the size of the cartesian coordinate system. The grid needs to have the origin centered in the canvas as it is now, and we should be able to adjust the size of the grid (it will remain square) and the size of the increments. Once this is working, we can focus on the parabola design.

Prompt Critique — Four Strengths, Five Areas to Improve

✔  Strengths

  1. Educational framing. Attaching onlyHadABrain2026.html and analyzingOnlyHadAVertexLyrics.html gave Copilot genuine context for why the b coefficient matters. Copilot used this to connect the visual design to the Oz green & gold palette without being told to. Context does design work.
  2. Phased approach. “Once this is working, we can focus on the parabola design” is correct S.P.A.R.K. thinking. Phase 1 has one testable objective; scope is not allowed to creep. The phase boundary is clean and respected.
  3. Mathematical precision stated for Phase 2. Even though a, c, bmin, bmax, binc won’t be built until Phase 2, stating the full specification in the Phase 1 prompt helps Copilot design infrastructure that will support it. The grid’s adjustable range anticipates the parabola plotting needs.
  4. Starting point identified. “parabola1.html is a copy of a previous app” tells Copilot the codebase exists and can be inspected. This is better than describing the architecture in prose — it lets Copilot read the truth for itself.

⚠  Areas to Improve

  1. Ask for the plan before the build. In S.P.A.R.K., after stating your goal, the next message should be: “Before making any changes, list the files you would modify and describe what you would do to each.” This is the A step — Analyze before acting. A plan check-in surfaces surprises (like a large file rewrite) before anything is changed.
  2. Define acceptance criteria for “working.” The phrase “once this is working” is vague. A stronger prompt specifies: “Phase 1 is complete when: (1) the origin is centered, (2) the half-size slider adjusts the grid from ±5 to ±25, (3) the step dropdown changes grid spacing, and (4) numbered tick labels are visible and update on both controls.”
  3. Specify UI control types. Copilot chose a range slider for size and a select dropdown for step — reasonable but unspecified. Novice collaborators learn more when they specify: “Use a range slider (min=5, max=25, step=5) for half-size and a select dropdown (options: 0.5, 1, 2, 5) for grid step.”
  4. Note the typo. The prompt has “controling” (one ‘l’). Preserved here authentically, as with Mason’s code. Typos rarely break Copilot’s output, but prompts are documents worth writing with care.
  5. Open with an explicit S step. The prompt jumps from app context straight to implementation. S.P.A.R.K. suggests opening with a goal statement first: “Goal: build a visual tool for studying how varying b shifts the vertex of y = ax² + bx + c. Phase 1 requirement: a functional Cartesian coordinate system. Requirements follow…” The S step is not a formality — it confirms that Copilot and the developer share the same goal before any work begins.
Copilot’s Analysis
🔍
A — Analyze: What Copilot Found First Before writing a single line of new code, Copilot read 6 files to understand the SketchWaveJS template architecture. This is the correct first move in S.P.A.R.K.’s A step: understand what exists before deciding what to change.
Files Explored Before Any Edits
FileKey finding
parabola1.html Right panel had 12 Glow Lab DOM elements to replace; swEllipse.js and swLine.js still loaded but unused
sketches/parabola1Sketch.js Contains ellipse/glow-path logic from previous app; variables ULx, ULy, LRx, LRy, stepX, stepY, fr seed the SWGrid in setup
sketches/swTemplate2Sketch.js Template owns setup(), draw(), wireControls(), canvas resize, and key events; calls user hooks initAppVars(), drawMyDesign(), resetAppDesign()
shapeClasses/swGrid.js SWGrid provides userToScreen(), screenToUser(), and draw() (axes + grid lines); no axis tick labels — those must be added in drawMyDesign()
styles/designerStyles.css Empty — available for additions without touching the template styles
sketches/ directory 7 sketch files present; only parabola1Sketch.js and swTemplate2Sketch.js are loaded by the HTML
Decision 1 — Repurpose showElements as the “Labels” Toggle

The template already wires showElements to the E key and a checkbox with id showElementsCheck. In the Glow Lab version this toggled focus points and focus lines. For the parabola app those elements don’t exist. Rather than add a new variable and new DOM element — which would require editing the template file — the existing hook was repurposed: the label changed from “Elements” to “Labels,” and drawMyDesign() uses if (showElements) drawAxisLabels(ctx.grid).

The E key shortcut, the checkbox wiring, and the reset behavior all come for free. No template changes needed. This is what working with the framework looks like instead of around it.

Decision 2 — Axis Tick Labels Were Not Requested — But Were Added

The prompt asked for a controllable grid. It said nothing about axis labels. Copilot added drawAxisLabels() anyway — a correct inference. A coordinate system without numbers is not usable for the educational goal: students need to read vertex x- and y-values from the graph. A grid without labels is a decoration, not a tool.

The implementation uses an adaptive density formula to prevent label overcrowding at small-step/large-grid combinations:

// Adaptive: prevents overcrowding for any slider/select combination
const totalTicks = Math.round((g.LR.x - g.UL.x) / g.xStep);
const labelEvery = totalTicks <= 12 ? 1 : totalTicks <= 30 ? 2 : 5;

Default grid (±10, step 1 → 20 ticks): labelEvery = 2, labels at −10, −8,… 8, 10. Small grid (±5, step 1 → 10 ticks): labelEvery = 1, every tick labeled. Large grid (±25, step 1 → 50 ticks): labelEvery = 5, one label every 5 units. This formula should be tested against all slider/select combinations before Phase 2 begins.

Decision 3 — Forest Green Theme (Inferred from Context)

The theme color #2a7a2a (forest green) was not specified in the prompt. Copilot chose it by connecting the attached context: onlyHadABrain2026.html and analyzingOnlyHadAVertexLyrics.html both use a deep forest-green palette. Aligning the Parabola Saga app to that palette makes the educational connection visible when both pages are open.

This is context doing design work for free. The more coherent the attached source material, the more coherent Copilot’s visual choices will be. No specification needed.

Decision 4 — showGridCheck Gets the checked Attribute

In the original Glow Lab HTML the grid checkbox had no checked attribute, so it appeared unchecked on initial render. The template’s wireControls() corrects this at runtime (it sets showGridCheck.checked = showGrid where DEFAULT_SHOW_GRID = true). The new HTML adds checked directly to the element so the visible state matches the correct default before JavaScript runs. A minor fix, but visible to any developer reading the source — and a clean habit to form: the HTML should reflect the intended initial state.

What Was Built
✏️
R — Refine: Phase 1 Result Two files substantially changed during Phase 1, plus two more updated during this chatlog session. A third file, styles/designerStyles.css, held the SPARK bar styles.
Files Changed
FileChangeWhen
sketches/parabola1Sketch.js Complete rewrite — 162 lines, 7 functions. All ellipse/glow code removed. New: initThemeColor, initAppVars, wireGridControls, applyGridSettings, drawMyDesign, drawAxisLabels, resetAppDesign Phase 1
parabola1.html Glow Lab card → Grid Settings card; modal Phase 1 content written; SPARK bar added; leftover GlowTrack modal paragraphs removed (bug fix) Phase 1 + this session
styles/designerStyles.css SPARK bar styles (.ps-spark-bar) added This session
parabolaSagaChatlog.html This file — created This session
styles/parabolaSagaChatlogStyles.css Chatlog stylesheet — created This session
Bug Found During Review — Leftover GlowTrack Content in Modal

The app info modal still contained two sections from the previous GlowTrack app that were not removed during the Phase 1 refactor. Anyone who opened the modal would see:

  • “Over time, the glow accumulates. A shape begins to emerge from the noise…” (GlowTrack description)
  • The “🔮 There IS a Secret” section describing draggable focus points F1 and F2 — which no longer exist in the app

Both were removed during this chatlog session. The modal now correctly describes Phase 1 only.

The lesson: after any refactor, run the app and interact with every UI element — buttons, modals, tooltips. Code that is never reached by automated tests must be verified by human review. Build → Run → Open every interactive element → Review is a habit worth teaching alongside the S.P.A.R.K. prompt strategy.

Noted for Phase 2 — Unused Script Tags

swEllipse.js and swLine.js are still loaded in parabola1.html from the GlowTrack era. They are harmless — the classes are defined but never instantiated — but they mislead anyone reading the source about what the app actually uses. Removing unused dependencies is a “Leave No Trace-y” discipline for HTML, not just Python Turtle. Phase 2 should clean these up.

Lessons Learned
💡
K — Know: Five Takeaways from Phase 1 One principle per key decision or shortcut taken in this session.
Session Takeaways
  1. Ask for the plan before the build. After the P prompt, send: “Before making any changes, list the files you would modify and describe each change.” This is S.P.A.R.K.’s A step made explicit. It makes Copilot’s reasoning visible and checkable before any file is rewritten.
  2. Phased development keeps prompts manageable. A prompt asking for the full parabola app would produce a large, hard-to-review result. Phase 1 = grid only. Phase 2 = parabolas. Each phase has one clear criterion for done. Large apps are built one phase at a time.
  3. Context guides design decisions for free. The attached onlyHadABrain context led directly to the forest green theme. Every piece of relevant source material attached to a prompt is a design decision you don’t have to specify explicitly. Copilot finds the connections when you give it the material.
  4. Review the full app after every phase. The GlowTrack modal text survived the Phase 1 refactor because no one opened the modal. Invisible UI elements (modals, tooltips, collapsed sections) require deliberate review. No automated check catches text that nobody reads.
  5. Work with the framework, not around it. Repurposing showElements for the Labels toggle required zero template changes. Reading the template first (the A step) revealed the hook; working with it kept the sketch clean and small. Framework hooks are infrastructure investments: understand them before building alternatives.
Phase 2: Single Parabola
📈
Phase 2 Prompt User made copies of the Phase 1 files as v2 (parabola2.html, parabola2Sketch.js, designerStyles2.css) and requested: SEO meta tags, a Parabola Design card with a/b/c sliders, graph one parabola, mark the vertex prominently on the canvas and in the controls. Stages should stand alone for comparison; parabola1.html untouched.
klp (TTG)

I made copies of the parabola1 software, as versions ‘2’. Let’s provide SEO information on this page and include a section in the controls, Parabola Design, where we provide sliders for a, b and c in a quadratic function so we can graph 1 parabola. The vertex should be marked on the graph as a more prominent point and listed in the controls. From there we can adjust to show multiple parabolas with variable b’s.

Let’s update the chatlog as well. There should be no need to adjust the parabola1.html and associated files since we have copies. I want each stage to be able to ‘stand alone’ for comparison purposes.

Prompt Critique — Three Strengths, Three Improvements

✔  Strengths

  1. Stage isolation enforced. “Each stage should stand alone for comparison purposes” is the right architectural instinct. parabola1.html remains unchanged. Open both in side-by-side tabs to compare Phase 1 (grid only) with Phase 2 (grid + parabola) at any time.
  2. Forward scope stated. “From there we can adjust to show multiple parabolas with variable b’s” signals Phase 3 clearly. Copilot can design Phase 2 infrastructure with Phase 3 in mind — drawParabola(g, a, b, c) takes parameters rather than reading globals, so Phase 3 just loops over b values.
  3. Scope limited. “Graph 1 parabola” is clean and achievable. No feature creep into Phase 3 territory.

⚠  Areas to Improve

  1. “More prominent” is vague. What does prominent mean? Larger circle? Different color? Labeled? A stronger prompt specifies: “Mark the vertex with a labeled red circle of roughly 10px inner radius with a white halo.” Copilot made reasonable choices, but explicit criteria leave less to chance.
  2. Slider ranges unspecified. Copilot chose a: −3 to 3, b: −10 to 10, c: −10 to 10 by inference from the ±10 default grid. Specify ranges explicitly in Phase 3 to avoid another unspecified decision.
  3. Copy/paste artifact found. The copied parabola2.html hero still said “(v1)”. A quick visual scan of the copy before prompting catches these trivially. The Build → Run → Review habit applies to copied files too.
Decision 1 — 600-Segment Polyline for Smooth Rendering

The parabola is a 600-point polyline via p5.js beginShape() / vertex() / endShape(). With a ±10 grid (20 user units across), 600 segments = 30 per unit — visually smooth at any slider setting.

For larger grids (±25, 50 user units), 600 segments gives 12 per unit, which may appear angular on steep parabolas. Phase 3 could scale segment count with gridHalfSize * 60 instead of a fixed 600.

Decision 2 — Warm Gold Curve, Red-Orange Vertex

The parabola uses HSB stroke(35, 90, 85) — warm gold/orange. This contrasts with the gray canvas background and dark green grid without clashing with either, and ties visually to the Oz gold accent throughout the project. The vertex marker uses red-orange fill(5, 88, 92) inside a white halo (fill(0, 0, 100, 88)), making it pop from both the curve and the background.

Decision 3 — Live Equation Display with Coefficient Colors

The top of the Parabola Design card shows y = ax² + bx + c with each coefficient in the same color used in the “If I Only Had a Vertex” parody display on onlyHadABrain2026.html. Students who read the lyrics analysis recognize the color coding. The direction indicator (“Opens UP — minimum”) connects the a sign to Verse 2 of the parody, closing the loop from text to interactive tool.

Decision 4 — Single onCoeffChange Handler

All three sliders share one handler. When any slider moves: (1) all three coefficient variables are read from their sliders, (2) all three display spans are updated, (3) updateVertexDisplay() recomputes h and k, (4) one canvas redraw is triggered. A separate handler per slider risks the equation display or vertex showing stale values if one update fires before another. One handler, one source of truth, one redraw.

✏️
R — Phase 2 Result Two app files updated; SEO added; unused script tags cleaned up (Phase 1 reminder actioned).
Files Changed in Phase 2
FileChange
sketches/parabola2Sketch.js Complete rewrite — parabola drawing (600-segment polyline), vertex marker, reactive coefficient controls, direction indicator, vertex bounds check
parabola2.html SEO meta tags added; hero “(v1)” → “(v2)” fixed; SPARK bar updated; Parabola Design card added; swLine.js + swArch.js removed; modal updated for Phase 2
parabolaSagaChatlog.html Phase 2 chronicle added; Phase 3 preview updated
💡
K — Phase 2 Takeaways
Phase 2 Takeaways
  1. Review copied files before prompting. A quick scan of parabola2.html before sending the prompt would have caught the “(v1)” artifact in the hero. Copy → Scan → Prompt is cheaper than Post-build bug discovery.
  2. One handler prevents stale state. onCoeffChange() is the single reactive entry point for all three sliders. When you have N controls that must stay in sync, one shared handler beats N individual ones.
  3. Color choices can be educational signals. Reusing the coeff-a (green) / coeff-b (blue) / coeff-c (red) colors from onlyHadABrain2026.html creates a visual bridge between the app and the parody page. Students see the same colors in both contexts.
  4. Phase 2 is ready for Phase 3. drawParabola(g) reads globals today, but the vertex math and rendering are self-contained. Phase 3 promotes the function to accept (g, a, b, c) parameters and calls it in a loop over b values — a minimal structural change.
Phase 2 Addendum: The Parabola Within
🧮
A — A Developer Observation Worth Proving While exploring the Phase 2 app, a question arose: as b is varied and the vertex moves, does the vertex trace a parabola of its own? The answer is yes — and the proof is two lines of algebra. The observation also prompted two new controls: a Show Vertex toggle and a Trail slider.
klp (TTG)

Since we have the vertex showing in the control panel, let’s include a checkbox in that region that can hide or show it on the actual graph. Let’s default that to not showing as we get ready to draw multiple parabolas.

Also, it dawned on me that if we had an opacity setting on the background redrawing, we could see the trail of the parabola as it is moved with the sliders. It appears to me that the trajectory of the vertex is itself on a parabola? Am I right? Let’s add this discussion to the chatlog.

The Math: Yes — the Vertex Traces a Parabola

Given the family y = ax² + bx + c with a and c fixed and b varying, the vertex is at (h, k) where h = −b / (2a). Substituting b = −2ah:

k = a·h² + b·h + c
  = a·h² + (−2ah)·h + c
  = ah² − 2ah² + c
  = −ah² + c

// The vertex (h, k) always satisfies:  k = −ah² + c
// i.e., the vertex trace IS the parabola  y = −ax² + c

The vertex trail is always the parabola y = −ax² + c. Three things to notice:

  • It opens the opposite direction from the family. If the family opens up (a > 0), the vertex trail is a downward arch. This makes visual sense: the vertex rises as the parabola narrows, and falls toward ±∞ as the vertex moves off-axis.
  • It passes through (0, c) — the y-intercept shared by every member of the family. When b = 0, h = 0, k = c, and the vertex sits exactly on the y-axis at the common y-intercept.
  • It depends only on a and c, not on b. The trail parabola is a fixed curve; b just determines where along it the vertex currently sits.
Decision — Trail via Semi-Transparent Background

The classic p5.js trail technique: instead of painting the background fully opaque each frame, paint it at reduced alpha. Old pixels fade toward the background color on each redraw, leaving ghost images of previous positions.

// Template calls this every draw() — alpha controls how much of the
// previous frame survives.  100 = full clear (no trail); 15 = strong trail.
bgCurrentColor = SWColor.fromHex(bgHex, bgTrailOpacity);
background(bgCurrentColor.col);  // semi-transparent → old parabolas fade

In noLoop() mode, redraws only fire when a slider is dragged. Each drag event fires a redraw(), building up the trail as the slider moves. When the slider stops, the trail freezes at the current state — until Reset clears it or the trail slider is set back to 0.

Educational payoff: dragging the b slider from −10 to 10 with a medium trail setting reveals the family of parabolas organically — a hand-drawn preview of Phase 3. The vertex trace parabola (y = −ax² + c) emerges visually as the cluster of vertex dots arches over the canvas.

Decision — Vertex Dot Always On; Label Checkbox Off by Default

After a clarifying exchange, the design was refined: the vertex dot (white halo + red-orange fill) is always drawn when the vertex is within grid bounds. The Show vertex label on graph checkbox controls only the V( h, k ) text annotation — nothing else.

The label defaults to unchecked because Phase 3 will draw one parabola per b value. Labeling every vertex would clutter the canvas, while the bare dots form a clean visual trail that reveals the vertex parabola y = −ax² + c organically.

Phase 2 Addendum: Files Changed
FileChange
sketches/parabola2Sketch.js showVertex flag (default false); bgTrailOpacity variable; showVertexCheck and trailSlider wired in wireParabolaControls(); vertex label (not dot) gated on showVertex; dot always drawn; both reset in resetAppDesign()
parabola2.html Show Vertex checkbox (unchecked default) and Trail slider (0–85%) added to Parabola Design card
Phase 2 Addendum: Panel Order & Progressive Disclosure
klp (TTG)

Since it’s less likely we will be changing the grid size, let’s put a toggle in the grid settings heading that will hide/show that panel so the Parabola Design will be more prominent. In fact, what do you think about making the Parabola Design panel first, with the grid settings after?

Decision — Parabola Design First: Information Hierarchy

The observation is correct: the grid is configured once (rarely changed thereafter); the parabola coefficients are the primary interaction point. Placing Parabola Design first means students immediately see the equation y = ax² + bx + c and its sliders — not the coordinate system size.

This is the information hierarchy principle applied to UI layout: the most important controls occupy the most prominent position. Secondary configuration (grid size, step, background color) belongs below the primary task, visible on demand.

Decision — Collapsible Grid Settings: Progressive Disclosure

Grid Settings collapses via Bootstrap’s built-in data-bs-toggle="collapse". The card header gains a small chevron button (▾) that rotates 180° when the panel opens, using a CSS transition on .ps-grid-toggle. Default state is collapsed (aria-expanded="false").

This is the progressive disclosure pattern: show what’s needed for the primary task, hide secondary configuration until the student chooses to access it. Grid settings still exist and are fully accessible — they just don’t compete for attention during the core exploration of how b affects the parabola.

Phase 2 Addendum: Start & Reset Button Roles
klp (TTG)

I notice that the ‘Reset’ button does not appear to do anything. In fact, although we may need it in future versions, the ‘Start’ button is not really needed. We could just hide it for now, neuter it, so it can be implemented later. Give me your suggestions on the role of the ‘Reset’ button with the removal of the ‘Start’ button.

Diagnosis — Why Reset Is Silently Broken

The template’s Reset button starts disabled in the HTML and only becomes enabled after Start is clicked (via syncStartPauseBtn()). After each Reset click, the template re-disables it with resetBtn.disabled = true. Since Phase 2 users never need to click Start — the app is reactive (slider → redraw() in noLoop mode), not animated — Reset stays permanently disabled. No error, no warning; it just sits gray and inert.

This is a “designed for animation” assumption baked into the template. The template assumes the flow: Start → dirty state → Reset. Phase 2’s flow is: slider drag → dirty state → Reset, with no Start in between. The template can’t know that.

Decision — Neuter Start with d-none; Fix Reset with enableReset()

Start is hidden with Bootstrap’s d-none class rather than removed from the HTML. All template wiring remains intact. When Phase 3 adds animation (e.g., sweeping b from bmin to bmax automatically), Start can be restored by removing d-none. Neutered, not deleted.

Reset is fixed with a small enableReset() helper called after every control interaction (sliders, checkboxes, trail). The resulting UX pattern is correct: drag any slider → Reset enables → click Reset → Reset disables → drag again → Reset enables. Reset is only available when there is something to reset — which is the right behavior.

Phase 3 role of Start: if Phase 3 animates b sweeping with loop(), un-hiding Start (removing d-none) is the only HTML change needed. The infrastructure — the Start handler, running flag, loop()/noLoop() coordination — is already there, just dormant.

Bug Found When Reset Was Tested — DEFAULT_PATH_HUE ReferenceError

Clicking Reset threw an uncaught ReferenceError: DEFAULT_PATH_HUE is not defined in swTemplate2Sketch.js. The template’s reset handler assigns a list of legacy GlowTrack variables — pathHue, pixelStrokeWt, revealTarget, and their corresponding DEFAULT_* constants — that parabola2Sketch.js never defines because the parabola app has no glow path, no pixel stroke weight, and no reveal target.

Why it was invisible until Reset was clicked: JavaScript event handler callbacks are closures — they capture variable names by reference, not values. The ReferenceError is only evaluated when the handler actually executes (on click), not when it is registered (on page load). This is called a deferred reference error: the code is syntactically valid at registration time but fails at runtime.

The fix: add stub constants to parabola2Sketch.js to satisfy the template’s implicit contract. The values are arbitrary since the corresponding UI elements (pathHuePicker, pixelStrokeWtSlider, etc.) do not exist in parabola2.html and are all null-checked before use.

The lesson: a shared template has an implicit contract — a set of global variables it expects the sketch to define. Read the template’s wireControls() and its reset handler before writing a new sketch. Every DEFAULT_* constant referenced there is a contract term. Failing to define one causes a silent bug that only surfaces on first Reset click.

Phase 3: b-Coefficient Family
📈
Phase 3 Prompt User requested: b min, b max, and b inc inputs; two color pickers (bmin color and bmax color); the resurrected Start button to trigger the family draw; vertex locus y = −ax² + c as a dashed overlay; stage nav links updated across all pages; chatlog and news entry updated. Parabola1 and Parabola2 unchanged.
Decision 1 — stopImmediatePropagation(): Intercepting the Template’s Start Handler

The template’s wireControls() wires the Start button to toggle running and call loop()/noLoop(). Phase 3 has no animation loop — it draws the entire family at once and stays in noLoop() mode. The two wiring approaches are incompatible.

The fix: in initAppVars() — which runs before wireControls() in the template’s setup() — add a click listener with e.stopImmediatePropagation(). Because listeners fire in registration order, and stopImmediatePropagation() cancels all later listeners on the same element, the template’s handler never fires:

startBtn.addEventListener('click', function(e) {
    e.stopImmediatePropagation();  // blocks template's loop toggle
    familyStarted = true;
    bValues = computeBValues();
    enableReset();
    refreshCanvasImmediate();
});

Teaching point: stopImmediatePropagation() prevents later listeners on the same target from firing. stopPropagation() only prevents the event from bubbling to parent elements — a common confusion. The distinction matters any time two unrelated libraries both want to handle the same click.

Decision 2 — lerpColor() in HSB Mode for Smooth Hue Transitions

The two color pickers return CSS hex strings. p5.js’s color('#hexvalue') parses them into color objects regardless of the current colorMode(). lerpColor(ca, cb, t) then interpolates in the current color space — HSB here. The result is a smooth hue sweep rather than a muddy linear RGB midpoint:

function familyColor(i, n) {
    const t = n > 1 ? i / (n - 1) : 0;
    return lerpColor(color(startColorHex), color(endColorHex), t);
}

A gold → blue picker pair sweeps through green and cyan — a natural rainbow arc across the family. Students can observe that the midpoint hue is determined by the shorter or longer arc around the hue wheel depending on which direction HSB interpolates. This is a subtle but real color theory lesson hiding in the slider.

Decision 3 — Dashed Vertex Locus via drawingContext.setLineDash()

p5.js has no native dashed-line API; dashes require dropping down to the Canvas 2D context. drawingContext is p5’s direct reference to the underlying CanvasRenderingContext2D. The pattern: save state, set the dash array, draw, restore:

drawingContext.save();
drawingContext.setLineDash([7, 4]);   // 7px on, 4px off
stroke(0, 0, 100, 82);               // near-white in HSB
beginShape();
// ... vertex() calls ...
endShape();
drawingContext.restore();             // solid lines for everything after

The save()/restore() pair is essential — without it, every line drawn after the locus (including the grid on the next frame) inherits the dash pattern. This is the same principle as p5’s push()/pop(), but for the raw 2D context instead of p5’s state stack.

Decision 4 — Color-Matched Vertex Dots & noLoop Throughout

Each vertex dot is filled with the same lerpColor() result as its parabola, not a fixed red-orange. This makes the color gradient legible at the vertex level as well as the curve level — students can follow any individual dot up the locus arch and trace it back to its parabola by color.

The app stays in noLoop() throughout. Drawing all N parabolas at 600 segments each (up to 200 × 600 = 120,000 vertex() calls) is a one-time operation; looping at 30fps would waste CPU for no visual benefit. Reactive redraws via refreshCanvasImmediate() are triggered by each control change, giving full interactivity without a render loop.

Decision 5 — Number Inputs for b Range; 200-Parabola Cap

Range sliders work well for single continuous values (Phase 2’s a, b, c). For bmin, bmax, and binc, number inputs are more appropriate: the user needs to set exact values, and the relationship between the three determines the count. A slider for binc with values from 0.1 to 5 would have 50 positions; a number input with step=0.1 is more precise and easier to type into.

The computed count is displayed live below the inputs. With binc = 0.05 and a ±10 range, that’s 401 parabolas × 600 segments. The safety cap at 200 keeps worst-case rendering bounded. The cap is shown as “N (cap)” in the count display so the user knows it was hit.

✏️
R — Phase 3 Result Three new files; five existing files updated (two app stage navs, chatlog, news entry, swExamplesIndex).
Files in Phase 3
FileChange
parabola3.htmlNew — Phase 3 app page
sketches/parabola3Sketch.jsNew — family draw, lerpColor, dashed locus, noLoop architecture
styles/designerStyles3.cssNew — Phase 3 styles (same Oz green/gold theme)
parabola1.htmlStage nav: v3 “coming” span → link
parabola2.htmlStage nav: v3 “coming” span → link
parabolaSagaChatlog.htmlStage navs updated; Phase 3 section added
news.htmlEntry news-2026-065: Phase 3 paragraph and link updated to “live”
💡
K — Phase 3 Takeaways
Phase 3 Takeaways
  1. stopImmediatePropagation() vs. stopPropagation(). When you need to block a second listener on the same element — not just prevent bubbling — use stopImmediatePropagation(). Register your listener first; later ones are silenced.
  2. Template hooks are ordered. initAppVars() runs before wireControls(). Registering a listener in initAppVars() means it always fires before the template’s listener on the same element. This is a reliable pattern for adapting template behavior without modifying template source.
  3. HSB interpolation gives better color transitions than linear RGB. lerpColor() in HSB mode produces smooth hue shifts. The “muddy midpoint” problem (red + blue = gray in RGB) disappears — instead you get a clean arc through the color wheel.
  4. drawingContext unlocks Canvas 2D features. Dashed lines, shadows, and other Canvas 2D context features unavailable in p5.js are accessible via drawingContext. Always save()/restore() around such calls — context state leaks globally, including to the next frame.
  5. Number inputs beat sliders for multi-variable relationships. When the interaction of three inputs (min, max, step) determines a derived quantity (count), number inputs with live feedback (“11 parabolas”) are clearer than three sliders with no derived display.