Skip to content

fix(LetterGlitch): smooth colour transitions freeze after one frame - #1070

Open
FelipeKreulich wants to merge 1 commit into
DavidHDev:mainfrom
FelipeKreulich:feat/fix-letter-glitch-smooth-transition
Open

fix(LetterGlitch): smooth colour transitions freeze after one frame#1070
FelipeKreulich wants to merge 1 commit into
DavidHDev:mainfrom
FelipeKreulich:feat/fix-letter-glitch-smooth-transition

Conversation

@FelipeKreulich

Copy link
Copy Markdown

Fixes #1069.

smooth was effectively doing nothing. The fade started and then stopped on the very next frame, leaving each letter about 5% of the way to its target colour until the next glitch tick swapped it out.

What was happening

handleSmoothTransitions read the fade's starting point from letter.color:

const startRgb = hexToRgb(letter.color);

but the line just below it overwrites that same field with interpolateColor(...), which returns rgb(r, g, b). From the second frame onwards hexToRgb was being handed its own formatted output, matched neither regex, and returned null — so the guarded branch never ran again. colorProgress still climbed to 1 and needsRedraw stayed false.

It's easy to miss because the canvas keeps animating regardless: updateLetters() swaps characters every glitchSpeed ms and calls drawLetters() on its own. Only the colour fade was missing.

What I changed

Interpolation now happens in numbers, and the CSS string is built only where it's used, at ctx.fillStyle. The parser is never handed its own output, so the failure can't come back.

Each letter now carries:

  • rgb — what is currently painted
  • fromRgb / targetRgb — the two endpoints of the fade

interpolateColor became mixRgb (returns {r, g, b}) plus rgbToCss.

Two things fall out of this that I think are improvements, but say the word if you'd rather I drop them:

  1. A letter picked again mid-fade now continues from the colour on screen instead of restarting from a value that was never displayed. updateLetters sets fromRgb to the letter's current rgb.
  2. An unparseable entry in glitchColors no longer stalls the animation. Previously it produced an invalid fillStyle and a frozen fade; now it falls back to white, which is visible rather than silent. I hit this by accident while testing — the docs page's ?colors= URL param splits the value incorrectly and passes #, f, f to the component. That looks like a separate demo-side bug, unrelated to this PR; happy to open an issue for it if it's news to you.

All four variants

content, tailwind, ts-default and ts-tailwind all carry the identical change. I checked first that they only differ in their styling layer, so the animation logic stays byte-identical across them. The four public/r/LetterGlitch-*.json files were regenerated with npm run registry:build rather than hand-edited.

npm run registry:build also rewrote public/r/DecryptedText-{JS,TS}-CSS.json, which looked like pre-existing drift between that component's source and its registry output. I left those out of this PR since they're unrelated.

Testing

Locally, in the docs app, sampling one letter cell over 90 frames and measuring the distance between consecutive colours:

distinct colours gradual steps hard jumps
smooth on 42 40 0
smooth off 7 0 6

Before the fix these two columns were near-identical, which is the bug. Now smooth on walks through intermediate tones, and smooth off jumps straight between palette colours — which is what each mode should do.

Repeated at a 360×640 container to cover the mobile resize path, since resizeCanvasinitializeLetters is where the new per-letter state is built from scratch: 43 distinct colours, 42 gradual steps, 0 hard jumps. Browser console is clean — no errors, and no new warnings (the only ones present are the pre-existing React Router v7 future-flag notices).

Type checking: both .tsx variants pass tsc --noEmit --strict.

Lint: npx eslint on the two .jsx variants reports nothing, before and after the change. (Repo-wide npm run lint reports 44 pre-existing problems in other components, none in these files.)

There's also a dependency-free Node reproduction of the original bug in #1069 if it's useful for confirming the diagnosis.

handleSmoothTransitions parsed letter.color with hexToRgb, but that same
field had already been overwritten with interpolateColor's `rgb(r, g, b)`
output on the previous frame. hexToRgb matches hex only, returned null, and
the guarded branch never ran again: colorProgress kept climbing to 1 while
the colour stayed ~5% of the way to its target, and needsRedraw stayed
false. In practice `smooth` had almost no effect.

Interpolation now happens in numbers and the CSS string is built only at
paint time, so the parser is never handed its own output. Each letter keeps
rgb (what is painted), fromRgb and targetRgb (the endpoints), which also
means a letter picked again mid-fade continues from the colour currently on
screen instead of jumping.

An unparseable entry in glitchColors previously left fillStyle invalid and
the fade frozen; it now falls back to white, which is visible rather than
silent.

Applied to all four variants, and the registry output regenerated with
`npm run registry:build`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG]: LetterGlitch smooth colour transition freezes after one frame

1 participant