Page MenuHomePhabricator

[Spike] Donor badge experiment: Evaluate implementation of delightful animation – feel, randomness, and color treatment
Closed, ResolvedPublic3 Estimated Story Points

Description

Background

Before committing to the full animation implementation, we need a spike to settle three open design questions and inform a few engineering questions:

  1. randomness model (fully random vs. constrained spread),
  2. color treatment (flat red, two-tone, white outline), including randomness model
  3. burst timing (current 0/150/300ms vs. tighter or single burst).

These can only be resolved by evaluating an in-progress animation (possibly on a real device). These decisions affect whether the animation stays surprising on repeat taps – central to the re-engagement loop hypothesis.

Output is a self-contained HTML prototype – no MediaWiki dependencies, no build step – shared for design review. Closeness, but not production code.

Goal

Documented decisions on all questions we can implement directly, with design sign-off.
https://codepen.io/editor/Volker_E/pen/019e042b-0322-7e8b-9f82-8c5608b280c0

Requirements

  • HTML file (or CodePen), very limited external dependencies. Animation via requestAnimationFrame matching the production approach.
  • Configurations switchable.
  • Re-tap during animation fades current hearts out over 150ms and restarts immediately.
  • Check frame rate on a mid-range Android during burst peak.

Acceptance Criteria for Done

  • Prototype shared and reviewable on mobile without a build step.
  • Randomness, color, and burst timing changeable.
  • Tested on iOS Safari and Android Chrome.
  • Frame rate checked on mid-range Android; anything below 50fps ruled out.
  • Design sign-off.
  • Task comment with one decision per variable and rationale before closing.
  • Implementation updated with any scope changes before this task closes.

Details

Other Assignee
Volker_E

Event Timeline

@Volker_E I have made annotations visible in figma and I mentioned there that the "hide badge" pill will only appear while the animation is happening and disappears after it ends. We can work out the exact timing of animation.

HFan-WMF triaged this task as High priority.May 7 2026, 5:49 PM
HFan-WMF moved this task from Incoming to Needs refinement on the Reader Experience Team board.
Volker_E renamed this task from Donor badge experiment: Implement the delightful animation (and evaluate randomness) to [Spike] Donor badge experiment: Evaluate implementation of delightful animation – feel, randomness, and color treatment.May 8 2026, 8:49 PM
Volker_E claimed this task.
Volker_E updated the task description. (Show Details)
Volker_E updated the task description. (Show Details)
Volker_E set the point value for this task to 3.
Volker_E updated Other Assignee, added: Sneha.

It became very quickly clear that using an SVG for the hearts animation would shoot us in the foot on lower-range and mid-range smartphones.

For posterity:
Inlined SVG in CSS (1st iteration of heart burst)

  • The browser has to parse the SVG markup inside a url() data URI each time a heart element is created
  • Each dynamically created div gets its own copy of the SVG string in innerHTML, which means several dozen separate SVG DOM trees during a burst
  • Memory scales with heart count
  • will-change: transform, opacity on each element is fine, but the SVG inside adds to compositing cost

CSS shapes only (a heart via clip-path or ::before/::after with border-radius & transform)

  • Zero SVG parsing
  • No extra DOM nodes inside each heart element — the shape is just a paint instruction
  • GPU compositing is cheaper because the layer content is simpler
  • A heart can be approximated very well with two border-radius pseudo-elements rotated 45°, or precisely with a single clip-path: path(...) which is now well-supported

For currently ~35 animated elements flying around with requestAnimationFrame, the difference is measurable on mid-range Android.

Therefor I settled on CSS shape only.

From meeting with @Sneha today:

  • Animation - throw/Rainfall/other: We concluded that the current throw approach seems slightly more natural over the original rainfall animation, specifically in regards to slightly wider viewports on mobile and given that we need to exclude the full top bar in both approaches, which is also advantageous and more natural with throw
  • Randomization - direction, size, rotation, (other variations to play with - no. of hearts, color, mixed colors, other shapes, start location, etc.: We concluded to decrease largest size of hearts slightly, @Sneha will provide four colors to randomize towards, we will always start red but transition to the other colors during throw. Nice to have would be a possibility to provide a color change only on every nth tap, like every 6th. Other shapes are concluded to be a nice to have, we could focus on after the experiment concludes successfully.
  • Hearts visual - flat/gradient/opacity/heart shape etc.: I've shared that due to CSS only hearts we ran into technical limitations, and the CSS-only shapes are here to stay for performance reasons, but we'll introduce a opacity randomization as fourth randomization cue to start with as ideated by @Sneha
  • Wikiheart's button pushy animation @Sneha: 👌
  • Tapping again on heart while animation is on: @Sneha was asking to have more hearts added to the animation instead of throwing them away. My response was it's again a performance and non-blocking interaction limit here, but I'll try to fiddle with the fade-out at the end when tapping again, to make it more natural. Instead of throwing them immediately away. This last one is a nice to have and not a blocker for resolving here right now.

@Volker_E I think the animation is a thing of beauty! I love the randomness, especially that it's different each time, and the progression and fade feel very natural to me. Nice job with the CSS-only hearts. I skimmed the code and it seems fine; I'd do a more careful review during formal code review (I want to balance viewing this as an experiment with the reality that most temporary things aren't temporary in our ecosystem :) ) but wanted to say that I think this approach looks great.

Update from last few days:
@Sneha and myself have been confering to bring this to a great place.

  • updated heart color to a better in-between #d74032 for combo btw badge & flying hearts
  • updated harmonious color palette
  • improved the tapping transition behaviour
  • minimally adapted arc angles for heart delivery
  • introduced random tap color surprise with above
  • reduced hearts size
  • made heart burst increase hearts on each tap
  • added opacity randomness, with a special treatment to not make all hearts per throw too transparent, to give more depth to the whole animation
  • added tap cooldown phase, to make behaviour more natural
  • optimized JS for best practices
  • extensively documented all JS

@SToyofuku-WMF I loved that too, but it didn't make it in the last iteration sadly as it was too far from the others. We will bring it back in another form later again. 🔀

Volker_E updated Other Assignee, added: Volker_E; removed: Sneha.