# 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. ```sh 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`).