S.P.A.R.K. Chat Log  •  09/15/2026
S.P.A.R.K. with AI — Development Dialog

Building Ethan’s Crazed Emoji Showcase

From an original p5.js sketch to a live code showcase —
greenbar display, instance-mode adapter, and the animated canvas design decisions.

About This Log

This log documents the construction of ethansOriginalCrazyEmoji.html: a TNT showcase page that displays an original student p5.js sketch alongside its running animation. The CSS teaching focus is the retro greenbar code display (same technique as Mason’s Nerdy Icon). The key architectural challenge is new: unlike Mason’s Python output which was a static PNG, Ethan’s sketch is animated. The page must show the original code and run it live.

Watch for Prompt Critique boxes (amber) and Design Decision boxes (green). The most important decision here is the instance-mode adapter pattern — it resolves the p5.js global mode / DOM placement tension without touching Ethan’s original code.

The Goal
🎯
S — Set Goal Ethan wrote an original p5.js animated emoji sketch. The goal: give it a proper TNT showcase page that respects the original code, displays it alongside the running animation, and teaches the same CSS techniques as the Mason’s Nerdy Icon showcase.
The Prompt
💬
P — Prompt The user’s request, with the key constraints analyzed below.
klp (TTG)

We have an example of artwork done by mason, where we see the source code and the resulting image on an HTML/CSS/JavaScript/Bootstrap5 app. This time, I have a p5js app I’d like to feature. It has original sketch code (attached). I’d like to have a similar app, Ethan’s Crazed Emoji where the code is shown as it was in mason’s app with the live canvas next to the source code. I’ve got a hero image, cuckoo.jpg in the images sub folder for Ethan’s site. Like Mason’s app, we’d like to construct a chatlog chronicling this development. Let’s call the first page, ethansOriginalCrazyEmoji.html with styles ethansOriginalCrazyEmojiStyles.css. The chatlog can be ethansCrazedEmojiChatlog.html. We will want SEO for our new app.

Prompt Critique — One Critical Difference Stated Implicitly

The prompt says “like Mason’s app with the live canvas next to the source code.” Those two phrases carry a tension the prompt does not name explicitly. Mason’s app showed the code on the left and a static PNG on the right — the artwork was already finished; the page just displayed it. Ethan’s sketch is animated (spinning spiral eyes, wagging tongue). A static image would miss the entire point of the work. The request for a “live canvas” is the right instinct, and it unlocks a new architectural decision that Mason’s page never needed to make.

The instruction to “want SEO for our new app” is correct to include as an explicit requirement. SEO meta tags, Open Graph properties, and a meaningful description are often omitted from showcase pages when they are treated as internal documents. Including the requirement prevents that omission.

The hero image choice — cuckoo.jpg — was provided, not requested. The word “cuckoo” is colloquial for “crazed”; the image name is its own theming instruction. A student who reads the file name understands the page’s emotional register before the CSS is written.

Design Decisions
🔍
A — Analyze Five design decisions shaped the page. Decision 3 is the most technically significant.
Decision 1 — Live Canvas, Not Static Image

Mason’s app showed a pre-rendered PNG because Python Turtle Graphics produces a static bitmap. There is no live version to show. Ethan’s sketch is different in every way: it loops at ~60 frames per second, uses frameCount to animate, and its defining visual features (spinning spiral eyes, oscillating tongue) are only visible in motion.

A static screenshot of Ethan’s sketch would show a blurry smear where the spiral eyes are mid-rotation — identical to how a slow shutter on a spinning propeller produces a visual artifact instead of the actual shape. The only honest representation is the running canvas. The page therefore loads p5.js and runs the sketch live, right next to its source code.

Decision 2 — Dark Page for a Light Sketch

Mason’s page used a warm cream background because the Python emoji face is drawn on a light gray canvas and a dark page would create jarring contrast. Ethan’s sketch also uses background(240) (light gray). The same logic applies — but the “crazed” theme argues for something more dramatic.

Decision: dark navy background (#0a0b1a) with yellow accent (#ffcc00, the emoji face color). The light canvas appears as a glowing window against the dark page — a spotlight effect that makes the animation more visually striking than it would be on a cream background. The hero uses the same dark-navy-to-dark-red gradient as the page, framing cuckoo.jpg without competing with it.

Decision 3 — The Instance-Mode Adapter Pattern (Key Decision)

Ethan’s sketch uses p5.js global mode: setup(), draw(), and drawSpiralEye() are plain global functions. In global mode, createCanvas() appends the canvas to document.body — the very last element, not a specific column layout position.

To place the canvas in the right column, next to the greenbar code display, p5.js needs to know which container to target. This requires instance mode, where a sketch function receives a p object and attaches to a named <div>:

var ethanSketch = function(p) {
    p.setup = function() {
        let cnv = p.createCanvas(460, 400);
        cnv.parent('ethansCanvas'); // target container by id
        p.angleMode(p.DEGREES);
    };
    p.draw = function() { /* ... */ };
};
new p5(ethanSketch);

The critical question: does using instance mode mean modifying Ethan’s original code? No — and this distinction matters. The page separates two things:

  1. The code that is displayed in the greenbar is Ethan’s original global-mode source, stored in a JavaScript template literal ETHAN_CODE. It is passed to buildCodeDisplay() and rendered line-by-line in the greenbar. It is never executed.
  2. The code that runs is an inline instance-mode adapter that contains the same logic — same drawing commands, same math, same animation — but with p. prefixed throughout and a cnv.parent('ethansCanvas') call added. The original file sketches/origEmojiSketch.js is untouched.

The displayed code is authentic; the running code is adapted for context. Both are faithful to Ethan’s work. This is the same principle applied in the Magic 8 Ball refactor: preserve working, tested code; build a new presentation layer around it.

Decision 4 — Greenbar for JavaScript (Same Technique, Different Language)

Mason’s greenbar displayed Python. Ethan’s displays JavaScript. The CSS technique is identical: nth-child(6n±) for three-line group alternation, counter-reset / counter-increment for auto-generated line numbers, and ::before for the selection-proof gutter. The only thing that changes is the syntax highlighter.

For JavaScript, the comment detector scans each line character by character, tracking whether it is inside a single- or double-quoted string before treating // as a comment boundary. This prevents stroke(204, 136, 0) (which contains no //) from being confused with // comment, and prevents color strings like “#ffcc00” (which contain characters but not //) from triggering false positives. Keywords are highlighted with a simple word-boundary regex applied only to the non-comment portion of each line.

Decision 5 — Responsive Canvas Width

The original sketch uses createCanvas(500, 400). In the page layout, the canvas column is col-lg-5 — approximately 380–460px wide depending on viewport. A fixed 500px canvas would overflow on narrow screens.

The instance-mode adapter reads the container’s offsetWidth at setup time and clamps it: var w = Math.min(container.offsetWidth || 460, 460). The height is derived at 0.87× the width, maintaining the original 5:4 aspect ratio. At 460px wide, the canvas is 460×400 — identical to the original proportions. Below that, it scales down proportionally. The face (300×300) has comfortable margin at 460px and remains visible down to approximately 340px canvas width.

What the Session Produced
✏️
R — Refine Three files created. The original sketch file and hero image were already in place.
Files Created or Referenced
FileStatusNotes
ethansOriginalCrazyEmoji.html Created Main showcase page: hero, intro card, greenbar + live canvas, footer
styles/ethansOriginalCrazyEmojiStyles.css Created Dark-navy palette, greenbar CSS counters/nth-child, canvas panel, JS syntax classes
ethansCrazedEmojiChatlog.html Created This page
sketches/origEmojiSketch.js Pre-existing — untouched Ethan’s original p5.js sketch in global mode. Source of the ETHAN_CODE template literal; never executed by the page.
images/cuckoo.jpg Pre-existing — untouched Hero background. “Cuckoo” = crazed; the image name is the theming brief.
💡
K — Know Four principles from this session.
Session Takeaways
  1. When the output is animated, a static screenshot is not a faithful representation. Ethan’s spiral eyes and oscillating tongue are defined by their motion. A screenshot captures one arbitrary frame and discards everything that makes the sketch interesting. Always ask: is this output static or dynamic? Dynamic output requires a live canvas.
  2. The instance-mode adapter separates what is displayed from what runs. Storing the original code in a template literal and displaying it in the greenbar preserves authenticity. Running an instance-mode equivalent preserves correct placement. These are two separate concerns and should be solved separately. The original file is never executed; the running code is never displayed.
  3. The greenbar technique is language-agnostic. The CSS (nth-child, counter-reset, ::before) works identically for Python, JavaScript, Java, or any language. Only the comment-detection logic in the JavaScript highlighter changes. The visual infrastructure — alternating green stripes, auto-numbered gutter — is a pure CSS pattern that can be reused anywhere.
  4. Design the page tone to match the content. Mason’s cheerful Python emoji warranted a warm cream background. Ethan’s “crazed” theme — spinning eyes, asymmetric everything, “cuckoo.jpg” — calls for something more dramatic. Dark navy with yellow accent places the light sketch in a spotlight. The canvas is not sitting on the page; it is glowing from inside it. Tone is a design decision, not just an aesthetic preference.
Post-Build — Ecosystem Wiring
✏️
R — Refine Three ecosystem files updated after the initial build: Ethan’s app was registered in the two correct categories on Explore and announced on the News page.
klp (TTG)

Let’s add Ethan’s app to the explore page, categorized as a SPARK app and a processing app. The chatlog should only be listed in the SPARK area; both categories should ‘host’ the link in the offcanvas area since the cards are ‘full’. In the news page, let’s create a listing for the app above the most recent news entry about Quadratic Class Grows Up. Be sure to look at that most recent entry to follow styling protocols. Let’s add this prompt to our chatlog for Ethan’s app.

Post-Build Decision — Explore Page Wiring

The Explore page category cards were already full, so both S.P.A.R.K. and Processing entries were added to their respective offcanvas panels only.

S.P.A.R.K. offcanvas — Two entries added after the Twelve Days Parody chatlog entry:

  • Ethan’s Crazed Emoji (showcase link, lang-badge lang-js p5.js)
  • Ethan’s Crazed Emoji — Chat Log (chatlog link, lang-badge lang-js S.P.A.R.K.)

Processing offcanvas — One entry added after the Lemur Game entry (chatlog not duplicated here):

  • Ethan’s Crazed Emoji (showcase link, lang-badge lang-js p5.js)
Post-Build Decision — News Page Entry #069

A news entry was added to news.html as entry #069, inserted above entry #068 (Quadratic Class Grows Up). The entry title, date format, accordion structure, and link-with-icon style match the surrounding entries.

The entry summarises the four key points: the live canvas decision, the instance-mode adapter pattern, the original file being untouched, and the chatlog’s five design decisions. Two links close the entry: the showcase page (with a fa-face-grin-squint icon) and the chatlog (with fa-comments).

Files Modified in Post-Build Wiring
FileWhat Changed
explore.html S.P.A.R.K. offcanvas: two entries added (showcase + chatlog). Processing offcanvas: one entry added (showcase only).
news.html Entry #069 added above entry #068, following the established accordion and link-icon style.
ethansCrazedEmojiChatlog.html Post-build section added documenting the Explore and News wiring prompt and decisions.
Stage 2 Planning — Refactoring Roadmap
📋
A — Analyze Before writing a single line of the refactor, the experienced CS teacher and developer reads the original code carefully and produces a prioritized action-item list. The ordering matters: some changes unlock others.
klp (TTG)

I am very pleased with how you set up Ethan’s original crazy emoji. As part of our chatlog, I’d like you, as a professional experienced high school computer science teacher and professional web app developer, to create a prioritized list of ‘action items’ you’d perform to refactor this code to a professional status and, to leverage its content as a teaching/training/tutorial experience, especially when it comes to the idea of modularization by functions, scalability by using variables tied to the canvas size, and the concept of push and pop with transformations and rotations. Once this checklist is in place, we will begin a refactor of Ethan’s original code.

CS Teacher & Developer Perspective — Why Order Matters

The temptation when refactoring student code is to “fix everything at once.” That produces a rewrite, not a refactor, and it erases the teaching trail. Each phase below builds on the one before it, and each phase is a teachable standalone lesson. A student can run the code after each phase and see the same emoji — the output is the constant; the architecture improves.

The ordering follows a fundamental rule of software engineering: structure before scale before style. You cannot meaningfully scale code that has no structure. You cannot add teaching enhancements to code that is not already readable. Phase 1 creates the structure. Phase 2 names the parts. Phase 3 colorizes with intention. Phase 4 enforces transform discipline. Phase 5 earns scalability. Phase 6 makes it a teaching artifact.

Phase 1 — Modularization by Functions (Do First — Unlocks Everything Else)

Why first: Every other improvement is easier to make and easier to teach once each facial feature lives in its own named function. This is the architectural change that turns a monolithic draw() into a readable recipe.

  1. Extract drawFace() — the yellow ellipse and stroke. One feature, one function. A student can comment out the call and the face disappears but the eyes and mouth stay. That single observation teaches the lesson.
  2. Extract drawEyebrows() — the two arc() calls that create the wild asymmetric brows. These are standalone geometry with no state dependency on other features.
  3. Extract drawEyes() — both eye push/rotate/drawSpiralEye/pop blocks. drawSpiralEye() already exists as a helper; drawEyes() becomes its caller, managing the two translate/rotate transforms. This is the primary push/pop teaching function.
  4. Extract drawMouth() — the off-center dark ellipse. Simple geometry; good starter function for students writing their first extractions.
  5. Extract drawTongue() — the push/translate/rect/line/pop block. This is the primary sin()/cos() oscillation teaching function. Wrapping it in a named function gives the motion a name that students can search for and study.
  6. Rewrite draw() as a call list:
    function draw() {
      background(240);
      translate(width / 2, height / 2);  // center origin once
    
      drawFace();
      drawEyebrows();
      drawEyes();
      drawMouth();
      drawTongue();
    }//end draw
    This draw() reads like plain English. A student who has never seen the rest of the file can predict what it does from this alone.

Teaching payoff: Comment out drawEyes(). The face, brows, mouth, and tongue render; the eyes vanish. Restore it. Comment out drawTongue(). The emoji goes still. That hands-on cause-and-effect is the modularization lesson no slide deck can replace.

Phase 2 — Named Constants (Do Alongside or Immediately After Phase 1)

Why second: Once functions exist, the magic numbers are clearly owned by their feature. Named constants belong at the top as a “control panel” — one place to change one value and have everything respond. This is the DRY (Don’t Repeat Yourself) lesson.

  1. Declare face constants: var FACE_DIAM = 300;, var FACE_STROKE_W = 8;. Every reference to 300 in the original becomes FACE_DIAM.
  2. Declare eye constants: var EYE_L_X = -60; var EYE_L_Y = -20;, var EYE_R_X = 60; var EYE_R_Y = -10;, var EYE_L_SIZE = 50; var EYE_R_SIZE = 80;, var EYE_SPEED_MULT = 2;, var EYE_R_SPEED_MULT = 1.9;. That last one is the most important — the “1.9” was a creative choice Ethan made. A name honours that choice.
  3. Declare tongue constants: var TONGUE_SPEED_X = 8; var TONGUE_SPEED_Y = 12;, var TONGUE_DIST_X = 15; var TONGUE_DIST_Y = 8;. The multipliers inside and outside the sin()/cos() calls are the most educational constants in the entire sketch — changing them produces visually immediate feedback.
  4. Organize by feature at the top of the file: A block of face constants, then eye constants, then tongue constants. The file now has a “settings panel” a student can tune without reading the drawing code.

Teaching payoff: Change EYE_R_SPEED_MULT from 1.9 to 3.0. The right eye spins faster and the crazed effect intensifies. Change it to 1.0. Both eyes spin at the same rate and the asymmetry vanishes. One variable. Two dramatically different animations. This is why we name things.

Phase 3 — HSB Color Mode (After Named Constants — Makes Color Intent Human-Readable)

Why third: Phase 2 gives color values a name; Phase 3 gives those names a meaning any human can read. var FACE_HUE = 48 is only useful if the reader knows that 48° is yellow-orange. Without HSB mode, a student still needs a color picker to interpret the constant. The two phases complete each other: names become interpretable only once the color model speaks human.

One line in setup() switches the entire sketch from RGB to HSB:

colorMode(HSB, 360, 100, 100, 100);
// H: 0-360 (color wheel)   S: 0-100%   B: 0-100%   A: 0-100%

All color calls in the sketch then become self-documenting:

  1. Face fill(255, 204, 0)fill(48, 100, 100) — 48° = yellow-orange, 100% saturation, 100% brightness. Pure emoji yellow, stated explicitly.
  2. Brown outline stroke(90, 50, 10)stroke(30, 89, 35) — 30° = warm brown, 89% saturated, 35% bright = dark earthy brown. The darkness is directly readable from the brightness value alone.
  3. Mouth interior fill(80, 20, 20)fill(0, 75, 31) — 0° = red axis, 75% saturation, 31% brightness = deep blood-red. A student who reads 31 knows it is dark before running the code.
  4. Tongue fill(255, 80, 100)fill(353, 69, 100) — 353° = just past red into pink, 69% saturation, full brightness = vivid pink-red.
  5. Pair HSB with named constants from Phase 2: var FACE_HUE = 48; var FACE_SAT = 100; var FACE_BRI = 100;. The constant names are readable; the HSB values are interpretable. Together they form a self-documenting color palette — the full expression of Phase 2’s intent.

Teaching payoff: Change FACE_HUE from 48 to 180. The face turns cyan. Change FACE_SAT from 100 to 20. The face desaturates to near-white. Students are now composing color by thinking — hue, saturation, brightness — rather than guessing RGB combinations. That shift from guessing to knowing is what HSB mode teaches.

Phase 4 — Push/Pop Discipline — “Leave No Trace-y”

Why fourth: After Phase 1, functions exist but they may leave transforms in an inconsistent state. Phase 4 enforces the rule that every function that calls translate() or rotate() must open with push() and close with pop(). This makes every function independently commentable and independently testable — the “Leave No Trace-y” principle applied to p5.js.

The principle: when your function ends, the coordinate system should look exactly as it did when your function started. Your function should leave no trace on the transform stack.

  1. Audit every function for unclosed transforms. Ethan’s spiral eyes already use push/pop correctly — note that as a teaching compliment. The tongue already uses push/pop. Verify the other functions introduce no stray translations.
  2. The established pattern for every feature function:
    function drawEyes() {
      // Left Eye — push: save state; rotate; draw; pop: restore
      push();
        translate(EYE_L_X, EYE_L_Y);
        rotate(frameCount * EYE_SPEED_MULT);
        drawSpiralEye(EYE_L_SIZE);
      pop();
    
      // Right Eye — opposite spin, 1.9× faster
      push();
        translate(EYE_R_X, EYE_R_Y);
        rotate(-frameCount * EYE_SPEED_MULT * EYE_R_SPEED_MULT);
        drawSpiralEye(EYE_R_SIZE);
      pop();
    }//end drawEyes
    The indentation inside push/pop is optional style but makes the nesting visually explicit for students.
  3. Add a brief comment at each push/pop pair explaining what state is being saved and why it needs to be restored.
  4. Test by commenting out individual functions. After Phase 3, every function can be commented out independently and the remaining features still render correctly. That is the test. If commenting out one function breaks another, push/pop discipline is incomplete.

Teaching payoff: Remove one pop() from drawEyes(). Watch what happens to the mouth and tongue in subsequent frames. The accumulated transform makes them fly off-canvas. Restore the pop(). The sketch is immediately correct again. Students remember the lesson because they saw the breakage. This is the most visceral p5.js lesson available.

Phase 5 — Scalability Tied to Canvas Size

Why fifth: Only after the code is modular (Phase 1), named (Phase 2), colorized in HSB (Phase 3), and disciplined (Phase 4) does scalability become clean. Adding scale to messy code produces messy scaled code.

  1. Derive FACE_RADIUS from canvas width: var FACE_RADIUS = width * 0.30; — the face fills 60% of the canvas width. All other constants that were previously tied to FACE_DIAM = 300 become expressions of FACE_RADIUS. At 500px wide: FACE_RADIUS = 150 = half of 300. Exact match.
  2. Express positional constants as ratios of FACE_RADIUS: Eye X-offsets are approximately 40% of the face radius (EYE_L_X = -FACE_RADIUS * 0.40). Eye sizes are roughly 33% and 53% of the face diameter. Derive these from the original hardcoded values: 60/150 = 0.40, 50/300 = 0.167. The math is the lesson.
  3. Move constant calculation into setup(): Since width is not defined before createCanvas(), all derived constants must be computed after the canvas is created. This is itself a teaching moment: variable declaration order matters.
  4. Add windowResized():
    function windowResized() {
      resizeCanvas(windowWidth, windowHeight);
      recalcConstants();  // re-derive FACE_RADIUS and friends
    }//end windowResized
    Dragging the browser window resizes the emoji in real time. The first time a student sees that, they understand what “scalable” actually means.

Teaching payoff: Change createCanvas(500, 400) to createCanvas(800, 640). The emoji grows proportionally without touching a single drawing constant. Change it to createCanvas(250, 200). It shrinks. That is scalability: one change propagates everywhere.

Phase 6 — Teaching Enhancements & Breadcrumbs

Why last: Breadcrumbs and instructional comments are most useful when the code they describe is already clean. A breadcrumb in a tangle is noise. A breadcrumb in a well-structured function is a lesson.

  1. Add SHOULD_SHOW_BREADCRUMBS flag (the TNT pattern, demonstrated in the Quadratic Upgrade). One boolean at the top. true = every function announces itself in the DevTools Console; false = silent animation.
  2. Add console.log(“...drawFace...”) at the top of each function, gated by the flag. With 60 frames per second, this produces a flood of messages — which is itself a lesson in animation loops: draw() runs sixty times every second.
  3. Annotate the oscillation math in drawTongue():
    // sin/cos oscillation: multiplier INSIDE controls speed (8 = fast)
    // multiplier OUTSIDE controls distance (15px left/right swing)
    // two independent functions = non-repeating Lissajous-style wobble
    var tongueOffsetX = sin(frameCount * TONGUE_SPEED_X) * TONGUE_DIST_X;
    var tongueOffsetY = cos(frameCount * TONGUE_SPEED_Y) * TONGUE_DIST_Y;
  4. Consider a “Stage 3: Scaling” showcase page (following the Mason’s Nerdy Icon pattern) that shows the Stage 2 refactored code alongside the Stage 3 scaled version, side by side in separate browser tabs.

Teaching payoff: Open DevTools. Set SHOULD_SHOW_BREADCRUMBS = true. Watch the Console scroll. Count the messages per second. A student who does this has just measured 60fps with their own eyes. That is more memorable than any lecture about animation loops.

What Ethan Already Got Right — Build From These Strengths

Before the refactor begins, the honest teacher acknowledges what the student built correctly. Ethan’s original code demonstrates four professional instincts that are worth naming explicitly in the refactored version’s comments:

  1. Push/pop already used correctly for the spiral eyes and tongue. The pattern was applied in exactly the right places. The refactor formalises it everywhere; it does not introduce it.
  2. The helper function drawSpiralEye(size) is already parameterised and reusable. A student who wrote a helper function for a repeated operation without being told to do so has already understood one of the most important ideas in software engineering.
  3. The right eye spins faster and in the opposite direction (-spiralSpeed * 1.9). This is a deliberate creative decision that produces a specific visual effect. Ethan knew what he wanted and coded it precisely. The refactor names that decision (EYE_R_SPEED_MULT = 1.9) rather than erasing it.
  4. The tongue uses two independent oscillation functions (sin and cos with different multipliers). This is the Lissajous pattern — a technique used in engineering and physics visualisation. Ethan arrived at it empirically. The refactor names and explains it.

The rule for refactoring student code: honour what they built, name what they chose, and extend what they started. The original code is not a rough draft to be discarded; it is a foundation to be elevated.

Refactoring Sequence At a Glance
PhaseFocusCore LessonTest
1FunctionsOne feature = one function; draw() = recipeComment any one call; only that feature disappears
2Named ConstantsMagic numbers lie; names explain intentChange EYE_R_SPEED_MULT; see animation change immediately
3HSB Color ModeHSB maps to human color thinking; RGB maps to hardwareChange FACE_HUE; see the face shift color instantly
4Push/Pop DisciplineEvery transform opened must be closed; Leave No Trace-yRemove one pop(); see chaos; restore it; see order
5Canvas ScalabilityHardcoded values break at different sizes; ratios do notChange canvas size; emoji scales automatically
6Breadcrumbs & DocsAnnotated code teaches; silent code mystifiesSet breadcrumbs flag; watch the Console flood at 60fps
Stage 2 Planning — Refinements
✏️
R — Refine Two plan refinements: the “View the Showcase” button is consolidated to the end of the document, and HSB color mode is inserted as Phase 3 of the refactoring roadmap.
klp (TTG)

I notice in the chatlog that the yellow button-like link is sprinkled throughout the log. We only need it once, at the bottom of the document. Also, I failed to mention in my ‘laundry list’ of items that I’d like the design to be done with HSB colorMode rather than the default RGB mode that Ethan used. Where would you insert this in our prioritized list of action items? Let’s add this prompt to our chatlog, of course.

Refinement — Button Consolidation

The “View the Showcase” button serves a reader who has just finished reading. Two intermediate buttons implied the log was complete at those points — it was not. A single button at the very end of the document is more accurate (the log is done here) and less visually disruptive to a document meant to be read linearly.

Refinement — HSB Color Mode Placement (Phase 3)

Why Phase 3 — after Named Constants, before Push/Pop:

Phase 2 gives color values a name. Phase 3 gives those names a meaning a human can read. var FACE_HUE = 48 is only useful if the reader knows that 48° is yellow-orange. Without HSB, a student still needs a color picker to interpret the constant. The two phases complete each other: names become interpretable only once the color model speaks human.

HSB precedes Push/Pop (Phase 4) because it is a color model change, not a transform structure change. It touches every fill() and stroke() call in the file but does not touch the transform stack at all. Push/Pop’s scope is transforms only. Keeping the two lessons in separate phases keeps each one independently teachable: one phase changes how colors are expressed; the next changes how coordinate systems are managed. Two separate ideas; two separate lessons.

Phase 1 — Modularization by Functions
💬
P — Prompt Implementing Phase 1 of the refactoring roadmap: modularization by functions, with a standalone showcase page for side-by-side comparison with the original.
klp (TTG)

We discussed ways to upgrade Ethan’s Crazy Emoji app. In the chatlog, you mentioned Phase 1 as modularization, so the draw function would feel like a call list. Let’s implement this phase, creating ethanCrazedEmojiPhase1.html with ethansCrazedEmojiPhase1Styles.css and phase1EmojiSketch.js. I don’t want any of the original code adjusted, this is an additional app that should appear in the ‘back-bar’ of the original and the chatlog so we can easily navigate to each phase for comparison purposes. Please implement this phase with comments and update the chatlog so we can better learn how this is done.

Prompt Critique — Three Good Instincts Worth Naming

“I don’t want any of the original code adjusted” is the most important sentence in this prompt. A refactoring series only teaches if the original is preserved for comparison. The moment the original is overwritten, the before/after lesson disappears. The user correctly treats the original as a museum piece — viewable, untouchable, permanently available for comparison. This is the professional instinct: version control by file, not by overwrite.

“Appear in the back-bar of the original and the chatlog” is equally important. Navigation between phases is the mechanism that makes the series a teaching tool rather than a collection of disconnected files. A student who can click between Original and Phase 1 can see the difference instantly. A student who has to open folder listings cannot. Navigation is pedagogy.

“Update the chatlog so we can better learn how this is done” is the S.P.A.R.K. method applied to the development process itself — not just the code. The chatlog is not documentation for its own sake; it is a teaching artifact that explains why each decision was made, not just what was built.

Implementation Decision 1 — Three Files, One Purpose Each

Phase 1 required three new files. Each has a single clear responsibility:

  1. phase1EmojiSketch.js — The standalone, documented, global-mode p5.js refactor. This is the file a student would open, study, and run on its own (with a local HTML wrapper or p5.js web editor). It contains the full Phase 1 code with explanatory comments at every function and every push/pop pair. It is the teaching artifact.
  2. ethanCrazedEmojiPhase1.html — The showcase page. It displays the Phase 1 code in the greenbar (sourced from a PHASE1_CODE template literal, identical to ETHAN_CODE in the original) and runs the same logic as an instance-mode adapter to place the canvas in the correct column. The same architectural separation as the original: displayed code is authentic global-mode; running code is adapted for DOM placement.
  3. ethansCrazedEmojiPhase1Styles.css — A self-contained CSS file, identical to the original’s styles except the canvas target changes from #ethansCanvas to #phase1Canvas. Keeping separate CSS files means each page is independently maintainable — a future Phase 2 palette change does not accidentally alter Phase 1.
Implementation Decision 2 — What Phase 1 Extracted

Five feature functions were extracted from draw(), plus drawSpiralEye() was already a helper in the original and was kept. Here is what each extraction did:

FunctionWhat it containsTeaching focus
drawFace()Yellow ellipse + outline strokeSimplest extraction — good starter for students writing their first functions
drawEyebrows()Two arc() callsShows that a “feature” can be as small as two lines
drawEyes()Both push/rotate/drawSpiralEye/pop blocksPrimary push/pop teaching function — calls the helper after rotating
drawMouth()Off-center dark ellipseDemonstrates that a named function can be four lines and still earn its name
drawTongue()push / sin+cos translate / rect+line / popPrimary oscillation teaching function — the Lissajous wobble has a name now

The result: draw() shrank from ~40 lines to 7. A student who reads those 7 lines knows the order of the emoji’s construction without reading a single drawing command.

Implementation Decision 3 — Comments as Teaching Instruments

Phase 1 adds comments that were absent from Ethan’s original. These are not retrospective documentation — they are forward-facing teaching annotations. Each push/pop pair now has an inline comment explaining what state is being saved and why it must be restored:

function drawEyes() {
  let spiralSpeed = frameCount * 2;

  push(); // save transform state before rotating left eye
  translate(-60, -20);
  rotate(spiralSpeed);            // spins clockwise
  drawSpiralEye(50);
  pop();  // restore — coordinate system is back at (0,0) before the right eye starts

  push(); // save again for right eye
  translate(60, -10);
  rotate(-spiralSpeed * 1.9);    // counter-clockwise at 1.9× speed — Ethan's deliberate choice
  drawSpiralEye(80);
  pop();  // restore
}//end drawEyes

The comment on rotate(-spiralSpeed * 1.9) is particularly important: it names Ethan’s creative decision as deliberate. A student reading this understands that the 1.9 is not an accident or a typo — it is a choice that Ethan made to produce a specific visual effect. The refactor honours that choice even while reorganizing the code around it.

Implementation Decision 4 — Navigation Between Phases

Three navigation changes were made so the pages form a coherent series:

  1. Original sketch spark-bar — added a Phase 1 link alongside the existing Chat Log link. The spark-bar is now a phase navigator: Original → Chat Log → Phase 1.
  2. Chatlog back-bar — added a Phase 1 link alongside the existing Original Sketch link. A reader who just finished the chatlog’s Phase 1 section can go directly to Phase 1 with one click.
  3. Phase 1 spark-bar — links back to Original Sketch and Chat Log. Phase 1 has no “next” link because Phase 2 does not exist yet. When it does, it will be added here.

The convention: every page in the series links to every other page that exists. As phases are added, each page’s spark-bar grows by one link. The series is self-navigating.

Files Created or Modified — Phase 1
FileStatusNotes
phase1EmojiSketch.js Created Standalone global-mode Phase 1 sketch with full teaching comments. Not executed by the page; displayed in the greenbar.
styles/ethansCrazedEmojiPhase1Styles.css Created Self-contained CSS for Phase 1 page. Identical to original styles except canvas target is #phase1Canvas.
ethanCrazedEmojiPhase1.html Created Phase 1 showcase page: greenbar displaying PHASE1_CODE, live instance-mode canvas in #phase1Canvas, before/after comparison block, and navigation links.
ethansOriginalCrazyEmoji.html Updated Phase 1 link added to spark-bar. Original code untouched.
ethansCrazedEmojiChatlog.html Updated Phase 1 link added to back-bar. Phase 1 implementation section added to chatlog content.
💡
K — Know Three principles from the Phase 1 implementation.
Phase 1 Takeaways
  1. A refactor that produces identical output is a safe refactor. Phase 1 changed nothing about what the emoji does — only how the code is organized. The test is simple: run the original; run Phase 1; observe that they are identical. If the output changes, the refactor introduced a bug. Same output = safe to proceed to Phase 2.
  2. Version control by file is the educational alternative to version control by git. The original sketch is preserved in ethansOriginalCrazyEmoji.html and sketches/origEmojiSketch.js. Phase 1 lives in its own page and sketch file. Neither overwrites the other. A student can open both in separate tabs and compare them visually and structurally. This is the pedagogical equivalent of a git diff — without requiring a command line.
  3. Navigation is part of the lesson design. A series of showcase pages without navigation is a collection of isolated documents. A series with navigation between every existing phase is a curriculum. The spark-bar links are not cosmetic — they are the mechanism that turns five separate HTML files into a single coherent learning experience.
Design Consistency — C.R.A.P. Principle: Repetition
✏️
R — Refine A visual inconsistency observed and corrected: the chatlog’s navigation bar used a different style from the spark-bars on the Original and Phase 1 pages. Applying the C.R.A.P. design principle of Repetition resolves it.
klp (TTG)

I like how the backbar is styled in the original page and in phase1. For consistency, let’s use that same approach in the chatlog as well. Add this observation to the chatlog, focusing on the idea of ‘Repetition’ as we’ve mentioned before with our ‘C.R.A.P.’ design approach at TNT.

Design Decision — C.R.A.P. Repetition Applied to Navigation

The C.R.A.P. design framework — Contrast, Repetition, Alignment, Proximity — is a foundational tool at TNT for evaluating visual design decisions without guessing. Of the four principles, Repetition is the one most often violated unintentionally: a designer creates a consistent visual element in one place, then builds something new somewhere else without returning to the original as a reference. The result is two things that look similar but not identical — which reads as a mistake rather than a choice.

That is exactly what happened here. The Original Sketch page and the Phase 1 page both use a .spark-bar with .spark-links wrapping the navigation anchors, a 1.2rem gap between links, and margin-left: auto pushing the page label to the right. The chatlog’s navigation bar was built separately using a .back-bar class with different spacing (0.5rem gap vs 1.2rem), justify-content: space-between instead of margin-left: auto, and no .spark-links wrapper. The links looked amber and bold in both — but the spacing, grouping, and hover behavior did not match.

The fix was three-part:

  1. CSS — Replaced the inline .back-bar rules with .spark-bar / .spark-links rules that exactly match the CSS in the showcase pages’ external stylesheets.
  2. HTML structure — Changed <div class="back-bar"> to <div class="spark-bar"> and wrapped the navigation anchors in a <div class="spark-links">, giving them the same grouping and gap behaviour as the other pages.
  3. Hover behaviour — Added color: #fff on hover to the links, which was present in the showcase pages but absent from the original chatlog styles.

The lesson Repetition teaches: a repeated element is only as strong as your commitment to repeating it precisely. “Similar” and “identical” are not the same thing in design. Similar creates visual noise; identical creates visual rhythm. When every page in a series uses the same navigation bar — same height, same colors, same spacing, same hover state — a user’s eyes learn to trust it. The nav becomes invisible infrastructure rather than something they have to read. That invisibility is the goal.

Phase 2 — Named Constants
💬
P — Prompt Implementing Phase 2: named constants replace every magic number, with forward-looking naming that anticipates Phase 3 (HSB colors) and Phase 5 (scalability).
klp (TTG)

You successfully created Phase 1 with its own styles and sketch. Let’s do this again for Phase 2, whose focus is on the idea of using ‘named constants.’ We need meaningful comments and adjustments that will lend themselves to future phase upgrades. Let’s address all of this in our chatlog for learning and development purposes.

Prompt Critique — One Phrase Does a Lot of Work

“Meaningful comments and adjustments that will lend themselves to future phase upgrades” is the most important phrase in this prompt. It shifts Phase 2 from a simple find-and-replace exercise into a forward-looking design task. A Phase 2 that only replaces 300 with FACE_DIAM is technically correct but pedagogically incomplete. A Phase 2 that annotates why each constant was named as it was — and what Phase 3 will do to the color constants and Phase 5 will do to the positional constants — turns the settings panel into a roadmap.

The instruction to “do this again” for Phase 2 implicitly requests the same three-file structure as Phase 1 (.js sketch, .css styles, .html showcase), the same spark-bar navigation pattern, and the same chatlog documentation approach. The pattern established in Phase 1 becomes the template for every subsequent phase. That is Repetition — the C.R.A.P. principle — applied to the development process itself, not just the visual design.

Implementation Decision 1 — The Settings Panel Concept

The key structural idea in Phase 2 is the settings panel: a block of named constants at the top of the file, organized by feature, that a student can tune without reading the drawing code. The drawing functions in Phase 1 had magic numbers embedded inside them. Phase 2 lifts those numbers out of their functions and gives them names and a home.

The organization matters as much as the naming. Constants are grouped under feature labels (// ── FACE ──, // ── EYES ──, etc.) so a student who wants to change the mouth can find all mouth constants in one place. The teaching payoff is immediate: change EYE_R_SPEED_MULT from 1.9 to 3.0 and watch the right eye spin dramatically faster. Change TONGUE_SPEED_X and TONGUE_DIST_X and observe the difference between how fast the tongue moves and how far it swings. These are experiments a student can run in 30 seconds — and they teach concepts that would take 30 minutes to explain in a lecture.

The BROWN_R/G/B constants deserve special mention. The dark-brown outline color (90, 50, 10) appears in four different functions: eyebrows, eye disc, mouth, and tongue. In Phase 1, that value was copied four times. In Phase 2, it is declared once as BROWN_R/G/B and referenced everywhere. This is the DRY (Don’t Repeat Yourself) principle made visible: one change to BROWN_G, and every outline color in the sketch responds simultaneously.

Implementation Decision 2 — Forward-Looking Naming for Phase 3

Color constants in Phase 2 use a _R/_G/_B suffix pattern: FACE_FILL_R = 255, FACE_FILL_G = 204, FACE_FILL_B = 0. This is intentionally verbose. Each constant is annotated with its Phase 3 equivalent:

// Phase 3: replace with fill(48, 100, 100) and stroke(30, 89, 35) in HSB mode
var FACE_FILL_R   = 255;  var FACE_FILL_G   = 204;  var FACE_FILL_B   =   0;
var FACE_STROKE_R = 204;  var FACE_STROKE_G = 136;  var FACE_STROKE_B =   0;

The _R/_G/_B naming is a deliberate signpost: a student doing Phase 3 can search the file for every constant ending in _R to find every color group that needs to change. Phase 3 will collapse each trio into a more readable HSB trio — FACE_FILL_R/G/B becomes FACE_HUE=48, FACE_SAT=100, FACE_BRI=100 — which is a significant reduction in line count alongside a dramatic improvement in readability.

The inline Phase 3 HSB values in the comments are not accidental. They give a student who reads Phase 2 a preview of what the values mean: 48° is yellow-orange, 100% saturation, 100% brightness — pure emoji yellow, stated explicitly. The comment is doing double duty: it documents the future change and it explains the current RGB values in human terms.

Implementation Decision 3 — Forward-Looking Naming for Phase 5

Positional constants are annotated with their Phase 5 equivalents:

// Phase 5: FACE_DIAM → width * 0.60 so the face fills 60% of any canvas width
var FACE_DIAM = 300;

// Phase 5: all eye positions and sizes → multiples of FACE_DIAM
var EYE_L_X = -60;   // will become -FACE_DIAM * 0.40
var EYE_L_SIZE = 50;  // will become FACE_DIAM * 0.167

These comments do something more than document the future: they teach the math behind scalability. A student who reads EYE_L_X = -60 and its annotation will become -FACE_DIAM * 0.40 can verify the ratio: 60 / 150 = 0.40 (150 is FACE_DIAM / 2 = the face radius). The constant encodes the ratio; the comment reveals it. Phase 5 does not invent new numbers — it makes the numbers that were always implicitly there explicit.

Implementation Decision 4 — Eyebrow Geometry: Intentionally Left Hardcoded

The eyebrow arc() calls retain hardcoded values in Phase 2:

arc(-60, -70, 60, 40, 200, 340); // left eyebrow  — Phase 5 will parametrize these values
arc(60, -50, 70, 40, 180, 310);  // right eyebrow — Phase 5 will parametrize these values

This was a deliberate decision, not an omission. Adding 12 new constants for eyebrow arcs would be technically correct but pedagogically noisy — six parameters per arc, two arcs, all of which are positional values that Phase 5 will derive from FACE_DIAM anyway. Naming them now as BROW_L_X = -60; BROW_L_Y = -70; etc. would give Phase 3 nothing to work with (eyebrows have no color constants that need HSB conversion) and would give Phase 5 a larger refactor rather than a smaller one.

The BROW_STROKE_W = 10 constant was extracted, because it is a single stylistic value a student might reasonably want to tune. The arc geometry was not, because it is a group of dependent positional values that only makes sense to parametrize all at once in Phase 5. This distinction — extract what a student would tune; leave what only makes sense as a group — is itself a design judgment worth discussing.

Files Created or Modified — Phase 2
FileStatusNotes
phase2EmojiSketch.js Created Standalone global-mode Phase 2 sketch. Settings panel organized by feature. Phase 3 and Phase 5 forward-looking comments throughout.
styles/ethansCrazedEmojiPhase2Styles.css Created Self-contained CSS for Phase 2 page. Canvas target is #phase2Canvas.
ethanCrazedEmojiPhase2.html Created Phase 2 showcase page: greenbar with PHASE2_CODE, live instance-mode canvas, before/after comparison, and teaching test.
ethansOriginalCrazyEmoji.html Updated Phase 2 link added to spark-bar.
ethanCrazedEmojiPhase1.html Updated Phase 2 link added to spark-bar. Phase 2 button added to next-card (Phase 2 now exists, so the “Up next” text is now a live link).
ethansCrazedEmojiChatlog.html Updated Phase 2 link added to spark-bar. Phase 2 implementation section added. Final buttons updated to include Phase 2.
💡
K — Know Three principles from the Phase 2 implementation.
Phase 2 Takeaways
  1. Named constants are most valuable when they anticipate change. A constant that is named for what it is today (e.g., FACE_DIAM = 300) is more useful than a comment explaining the magic number. A constant that is also annotated with what it will become in Phase 5 (width * 0.60) is more useful still. The best constants serve two purposes simultaneously: they make today’s code readable and they make tomorrow’s refactor easier to execute.
  2. The DRY principle compounds. The BROWN_R/G/B constant is used in four functions. In Phase 1, that color appeared four times as stroke(90, 50, 10). In Phase 3, when HSB replaces RGB, that change needs to happen once — not four times. Every repeated value that Phase 2 consolidates into a single named constant is a potential error point that Phase 3 no longer has to find.
  3. Not everything should be named yet. The eyebrow arc positions were left hardcoded in Phase 2 because extracting them now would produce 12 new constants that serve no purpose until Phase 5. Good refactoring is incremental — it extracts what is useful for the current phase and defers what is only useful for a later one. Over-extracting in Phase 2 would make the settings panel harder to read, not easier. The roadmap is the guide: if a constant isn’t needed until Phase 5, it is Phase 5’s job to introduce it.
Phase 3 — HSB Color Mode
💬
P — Prompt Implementing Phase 3: HSB color mode replaces RGB triples with H/S/B constants. An interactive color picker and stroke brightness slider bring the HSB lesson to life, driven by phase3EmojiFaceColorScript.js.
klp (TTG)

We have successfully implemented Phase 1 and 2 of Ethan’s Crazed Emoji app. You identified Phase 3 as incorporating HSB Color mode. Let’s do that, creating a group of Phase 3 documents that mirror the development we did for Phase 1 and 2. (Those phases should be kept unchanged; let’s develop Phase 3 as an independent app); Of course, let’s cross link all these pages as we did before and also update our chatlog as well. To illustrate how well our HSB mode works, why don’t we also include a color picker in the design that will let us change the color of the emoji’s face. Let’s make the stroke a percentage of the brightness of the face’s HSB and use a slider to control that. Let’s begin with a setting of 80%. For these controls, let’s create a JavaScript file, phase3EmojiFaceColorScript.js

Prompt Critique — Two Concepts, One Instruction

The prompt does two things at once: it requests a structural refactor (RGB → HSB) and an interactive feature (color picker + slider). These belong together because one teaches the concept and the other makes the concept tangible. A student who reads FACE_HUE = 48 understands it intellectually. A student who drags the color picker and watches fill(48, 100, 100) update in real time understands it experientially — the number and the color become the same thing.

The instruction to name the file phase3EmojiFaceColorScript.js before writing it forces a decision about responsibility: this file owns the DOM controls and the settings object. The p5 sketch owns the canvas. Neither knows about the other. That boundary is enforced by the file boundary — which is itself the lesson of separation of concerns.

The phrase “make the stroke a percentage of the brightness of the face’s HSB” defines a derived relationship, not two independent constants. In code: FACE_STROKE_BRI = FACE_BRI * STROKE_BRI_PCT / 100. The student who reads this line learns that some constants should be computed from other constants, not hardcoded independently. This is the most pedagogically significant sentence in the prompt.

Implementation Decision 1 — One New Line in setup()

The entire Phase 3 color model change is activated by a single line in setup():

function setup() {
  createCanvas(500, 400);
  angleMode(DEGREES);
  colorMode(HSB, 360, 100, 100);  // ← the one new line; all colors now speak in hue/sat/bri
}

From this point, every fill(a, b, c) and stroke(a, b, c) call interprets a as hue (0–360°), b as saturation (0–100%), and c as brightness (0–100%). The argument count is identical to RGB; only their meaning changes.

The pedagogical value of a one-line change is enormous. A student who compares Phase 2 to Phase 3 can find this single addition in under ten seconds. Everything else that changes (the constants) is a consequence of this one decision. That cause-and-effect is visible at a glance.

Implementation Decision 2 — RGB Triples Collapse to H/S/B Groups

Phase 2 had nine RGB triple declarations. Phase 3 collapses each triple into a compact, readable H/S/B group:

Phase 2 (RGB)Phase 3 (HSB)What it now says
FACE_FILL_R=255, G=204, B=0FACE_HUE=48, SAT=100, BRI=10048° = yellow-orange, fully saturated, full brightness
FACE_STROKE_R=204, G=136, B=0Derived: FACE_BRI × STROKE_BRI_PCT / 100Same hue/sat as face, 80% as bright — a rule, not a guess
BROWN_R=90, G=50, B=10BROWN_HUE=30, SAT=89, BRI=3530° = warm brown, 35% brightness = dark
MOUTH_FILL_R=80, G=20, B=20MOUTH_HUE=0, SAT=75, BRI=310° = red axis, 31% brightness = deep dark red
TONGUE_FILL_R=255, G=80, B=100TONGUE_HUE=353, SAT=69, BRI=100353° = pink-red, vivid full brightness

Black and white — previously expressed as fill(255) and stroke(0) — are now explicit: fill(0, 0, 100) (white: 0% saturation, 100% brightness) and stroke(0, 0, 0) (black: 0% brightness). Single-argument color calls behave unexpectedly in HSB mode; three-argument explicit forms remove all ambiguity and model the color correctly for students.

Implementation Decision 3 — Derived Stroke Brightness

In Phase 2, face fill and face stroke had six independent constants that implied a relationship nobody had named. Phase 3 names it:

var STROKE_BRI_PCT  = 80;  // stroke brightness as % of face brightness
var FACE_STROKE_BRI = FACE_BRI * STROKE_BRI_PCT / 100;  // = 80

Any student who reads these two lines understands not just what the value is but why it is what it is. When the interactive slider changes STROKE_BRI_PCT to 50, the face stroke darkens regardless of face color. The relationship holds across all colors — which is the definition of a proportional rule, not a hardcoded value.

Implementation Decision 4 — Separation of Concerns: phase3EmojiFaceColorScript.js

The interactive controls introduce a new architectural requirement: the p5 sketch must read externally controlled values every frame. The solution is window.phase3Settings — a shared bridge between two independent systems:

  1. phase3EmojiFaceColorScript.js — defines window.phase3Settings immediately on load (before the DOM is ready, so p5 setup finds it on the first frame), wires the color picker and slider, keeps the HSB code readouts in sync. Never touches the canvas.
  2. Inline p5 instance-mode sketch — reads window.phase3Settings inside drawFace() on every p.draw() call. Computes stroke brightness as Math.round(b * getStrokeBriPct() / 100). Never touches the DOM controls.

This separation means either piece can be modified independently. window.phase3Settings is the contract — a minimal API between two independent systems. It is also a concrete example of the design principle students will encounter in every major framework they ever use.

Implementation Decision 5 — Full Color Picker Over a Hue-Only Slider

Choosing <input type="color"> over a custom hue slider was deliberate. A hue slider locks saturation and brightness at 100% — the face would always be vivid. The native color picker allows pastels, desaturated tones, and near-blacks. When a student picks pale lavender and sees fill(270, 40, 90) in the readout, they understand from the numbers that it is a low-saturation, high-brightness purple — because they just chose it.

The controls script also implements the inverse HSB → hex conversion to synchronise the picker’s initial displayed color with the starting FACE_HUE=48, FACE_SAT=100, FACE_BRI=100 constants. This ensures the picker opens showing the emoji’s original yellow rather than defaulting to black.

Files Created or Modified — Phase 3
FileStatusNotes
phase3EmojiSketch.js Created Standalone global-mode Phase 3 sketch. Displayed in greenbar; not executed by the page.
phase3EmojiFaceColorScript.js Created Interactive controls: hex→HSB conversion, window.phase3Settings bridge, event handlers for color picker and stroke brightness slider.
styles/ethansCrazedEmojiPhase3Styles.css Created Page styles including .color-controls panel, .ctrl-readout code displays, and input range/color styling.
ethanCrazedEmojiPhase3.html Created Phase 3 showcase: greenbar with PHASE3_CODE, live instance-mode canvas, interactive color controls, before/after comparison.
ethansOriginalCrazyEmoji.html Updated Phase 3 link added to spark-bar.
ethanCrazedEmojiPhase1.html Updated Phase 3 link added to spark-bar.
ethanCrazedEmojiPhase2.html Updated Phase 3 link added to spark-bar. Phase 3 button added to next-card.
ethansCrazedEmojiChatlog.html Updated Phase 3 link added to spark-bar. Phase 3 section added. Final buttons updated.
💡
K — Know Three principles from the Phase 3 implementation.
Phase 3 Takeaways
  1. HSB makes colors composable. In RGB mode, changing the face color means changing three unrelated constants and hoping the new values are consistent. In HSB mode, changing FACE_HUE from 48 to 200 turns the face cyan while saturation, brightness, and the derived stroke relationship all remain intact automatically. The color model enforces consistency that RGB required manual discipline to maintain.
  2. A derived constant teaches more than a hardcoded one. FACE_STROKE_BRI = FACE_BRI * STROKE_BRI_PCT / 100 is not just a value — it is a rule. The stroke will always be proportionally darker than the fill, regardless of fill brightness. A student who reads this line understands the relationship once and never needs to manually maintain it again. Hardcoded constants require human vigilance; derived constants require understanding.
  3. Interactive controls turn abstract concepts into observable phenomena. A student who reads that HSB hue is measured in degrees might understand it intellectually. A student who drags from yellow (48°) to cyan (180°) to purple (270°) and sees the face change while the readout updates in real time understands it experientially. The color picker is not a feature; it is a demonstration. The stroke brightness slider is not a convenience; it is the derived relationship made tangible. Interactive controls are the p5.js version of hands-on lab work.