Files
scrabble-game/assets/vk/cover
Ilia Denisov 92a97c6c93 feat(vk): draft the VK community cover
Adds VK's own cover template (1920x768) and a self-contained generator that
renders an Эрудит board in perspective with ГРАМОТНОСТЬ / СОПЕРНИК /
ЭРУДИЦИЯ / УМ already played, in six art-direction variants for review.

The position is checked, not asserted: assertLegal re-reads the finished grid
and fails the build unless the centre is covered, every declared word is on the
board, and no maximal run of two or more letters is a word nobody played. All
four words are in the Эрудит dictionary and tile values come from
scrabble-solver rules.Erudit().

The camera has pitch only, so the perspective warp reduces to an exact
per-scanline remap of a flat texture; tile thickness is filled as 3D quads
between the board plane and the tile plane. skia-canvas is borrowed from the
render sidecar rather than installed a second time for a one-off asset build.

Composition respects the two VK keep-outs measured off the template: the top
130 px some clients crop, and the mobile avatar circle over the bottom-left —
which is why the default word arrangement keeps that corner empty.
2026-07-27 15:21:46 +02:00
..

VK community cover

The cover art for the VK community (1920×768). The community avatar — the «Э» tile — is uploaded separately; this is only the backdrop behind it.

build-cover.mjs is the whole thing: a small software renderer that draws an Эрудит board in perspective with the opening ГРАМОТНОСТЬ / СОПЕРНИК / ЭРУДИЦИЯ / УМ already played, and emits one PNG per art-direction variant plus a contact sheet.

cd assets/vk/cover
node build-cover.mjs                # every variant -> preview/
node build-cover.mjs night-spot     # one variant
FAST=1  node build-cover.mjs        # 1x instead of 2x supersampling, for quick iteration
ZONES=1 node build-cover.mjs        # overlay the VK keep-outs on the render
LAYOUT=cross node build-cover.mjs   # the alternative word arrangement

It needs no install: skia-canvas comes from the render sidecar's node_modules, and the letters are set in Spectral-Bold.ttf from the icon brand folder.

The position

ГРАМОТНОСТЬ opens through the centre square (its Т on H8) and the other three words hook onto a shared letter. assertLegal re-reads the finished grid and fails the build if the centre is uncovered, if a declared word is missing, or if any maximal run of two or more letters is a word nobody played — so the board on the cover is always a position the engine would accept. All four words are in the Эрудит dictionary; tile values come from scrabble-solver rules.Erudit().

The default ladder arrangement puts both long words horizontally. The cross arrangement hangs ЭРУДИЦИЯ down the left instead, which looks more symmetric but collides with the VK mobile avatar (below).

VK keep-outs

Measured off VK's own template (../VK_group_cover_template.fig), in cover pixels:

Zone Area Why
Top crop y < 130 trimmed by some clients — nothing important there
Mobile avatar circle, centre (283, 818), r 283 the community picture sits over the bottom-left in the VK app

ZONES=1 paints both so a composition can be checked against them.

Rendering technique

The camera has pitch only — no roll, no yaw — so a board-space horizontal line always maps to a screen-space horizontal line. That makes the perspective warp an exact per-scanline remap of a flat texture rather than a general homography: the board surface and the tile faces are each drawn once, flat, at high resolution, then replayed band by band (warpPlane). Tile thickness is filled as real 3D quads between the two planes, so the tiles have visible edges.

Two things that are easy to get wrong here and cost a hundredfold in render time: ctx.filter = 'blur(...)' applies per drawing call, so many blurred shapes must be collected into one offscreen layer and blurred once (blurred); and drawImage re-snapshots a live Canvas source on every call, so a texture sampled hundreds of times must be baked into an Image first (freeze).