Files
scrabble-game/assets/vk
Ilia Denisov 70b88c567f feat(vk): move the tile letter to the corner, add the light cover
Sets the letter in the tile's top-left corner with the value staying bottom
right — the in-game tile layout — instead of centring it. The glyph is placed
by its ink box rather than the text origin, so every letter lands on the same
corner whatever its side bearings and ascent.

Adds day-spot: the same crop and camera as night-spot on a light ground, so the
chosen composition exists as a light/dark pair.
2026-07-27 15:46:58 +02:00
..

Erudit — VK loading-screen logo (Lottie)

Animated logo for the VK app loading screen (shown before the SPA assets load). The Erudit «Э» tile (score 8) drops in under gravity, lands with a soft squash — its left/right edges bulging into a cushion — then springs back up, looping. A light "glint" is caught at the moment of the bounce.

File Purpose
erudit-loader.json The Lottie to upload to VK.
erudit-loader-preview.gif Looping preview. Rendered on a green "baize" background only in the preview — the real asset has a transparent background.
build/ The reproducible build pipeline (see Regenerate).

VK requirements met: 96×96 px · vector Lottie JSON · ≤ 24 KB (~11 KB) · seamless ~1.1 s loop · transparent background.

Design reference: a photo of the physical wooden Erudit tile.


How it works

A single flat layer holds the tile — a rounded-rect face path, the two glyphs, and a thin border — and everything is driven by that layer's transform plus a few colour / shape tracks. The anchor sits on the tile's bottom edge, so the squash happens against the floor.

Bounce (gravity)

position.y of the bottom edge goes apex → floor → apex with no dwell (the apex is an instantaneous turn-around). The fall uses a strong ease-in (accelerate) and the rise a strong ease-out (decelerate), so it reads as real gravity. While falling fast the tile slightly stretches (tall+thin); that is the squash-and-stretch setup.

Soft cushion (squash)

On contact the layer squashes (scaleY↓, scaleX↑) about the bottom anchor, with a touch of skew. Crucially the squash/cushion is decoupled from the fall speed — it eases in and out slowly (ES), so the landing feels soft even though the fall is fast. The squash is deliberately gentle (≈ 86 % / 112 %).

The cushion shape (the hard part)

The face is not a plain rounded rect — it is a path whose left/right contour bows out into a convex cushion on impact:

  • the rest rounded-rect outline is sampled (fine corner arcs + edge mids), and on impact every point is pushed outward by a smooth barrel profile f(y) = b·(1 (y/HH)²) (max at the middle, fading to zero at the top/bottom);
  • so the whole side — corners included — bows out as one piece, never just the straight middle;
  • bezier handles are computed as a chordal spline (handle length ∝ the adjacent chord), which stays smooth across the uneven corner/edge point spacing. This is what keeps the corner↔cushion junction kink-free — both at rest and fully bulged — which a naïve uniform Catmull-Rom (or moving discrete points with fixed tangents) does not.

Glint

At the bounce the face flashes a little lighter (FACE_GLINT) and the glyphs warm toward brown-red (BLACK_LIT), then settle back. A cheap, warm "catch the light".

Border & background

The tile carries a thin static border a touch darker than the face (BORDER) so it reads on any background. The green baize background is added only when rendering the preview GIF — the Lottie itself is transparent.

Player compatibility

Only plain 2-D shapes (ddd:0) — no 3-D layers, expressions or effects — so every Lottie player (lottie-web on the web, rlottie on native) renders it identically.


Glyphs

Э (U+042D) and 8 are real outlines from LiberationSans-Regular — metric- compatible with Arial, matching the game's --font: system-ui … Arial stack (see ui/src/app.css). build/extract.js pulls the contours into build/glyphs.json as Lottie cubic paths; a hair of same-colour stroke adds a touch of weight.


Regenerate

Requirements: Node ≥ 18. extract.js additionally needs opentype.js (npm i opentype.js); generate.js has no dependencies.

cd assets/vk

# 1. (optional) re-extract the glyphs — only if you change the font:
#    default font: /usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf
node build/extract.js [/path/to/font.ttf]      # -> build/glyphs.json

# 2. build the animation (reads build/glyphs.json):
node build/generate.js erudit-loader.json      # -> erudit-loader.json

# 3. (optional) live preview — needs lottie-web (npm i lottie-web):
node build/build-preview.js erudit-loader.json preview.html
#    then open preview.html in a browser.

The preview GIF is assembled by rendering the Lottie frame-by-frame (lottie-web canvas, filled with the green baize) and stitching with ffmpeg; the live HTML preview is the quickest way to eyeball changes.

Tunables (top of build/generate.js)

Symbol Meaning
OP, FR loop length (frames) / fps — overall speed
T_HIT / T_PEAK / T_LIFT / T_REC beats: contact / squash peak / lift-off / cushion recovered
EI / EO / ES easings: fast fall / fast rise / slow soft cushion
APEX, FLOOR bottom-edge screen-y at the top of the bounce / at rest
HW, HH, CR tile half-width / half-height / corner radius
scl squash values the squash amount (scaleX↑ / scaleY↓)
bulge b cushion depth (how far the sides bow out)
FACE / FACE_GLINT wood face / glint flash
BORDER tile rim colour
BLACK / BLACK_LIT glyph / glyph-at-glint (brown-red)
preview bg #3C7858 the green-baize colour used only in the GIF