Статистика платформи після хвилі 20
Хвиля-віха: хвиля 20 — це 20-та хвиля контенту платформи, 30-й навчальний допис (Навчальний #30 — Статистична теорія поля) і 40-й девлог. Навчальна серія тепер охоплює класичну механіку, електродинаміку, квантову механіку, спеціальну й загальну теорію відносності, термодинаміку, теорію хаосу та статистичну теорію поля «з нуля».
Ретроспектива хвилі 20
Хвиля 20 відкрила межу між біологією та фізикою. Спотлайт #40 відобразив імунну систему від толл-подібних рецепторів і комплементарного каскаду через V(D)J-рекомбінацію Т-клітин, дозрівання афінності антитіл і мРНК-вакцини до епідемічної динаміки SEIR та імунотерапії контрольних точок. Навчальний #30 розвинув статистичну теорію поля від континуального наближення Ландау-Гінзбурга моделі Ізінга через фейнманівські інтеграли по траєкторіях, ренормалізаційну групу Вілсона, класи універсальності та 2D-КТП, завершуючись глибокими зв'язками з енергетичними моделями машинного навчання й дифузійними генеративними моделями. Спотлайт #41 відобразив тверду Землю — шари PREM, час пробігу сейсмічних хвиль і тіньові зони, момент магнітуди, тектоніку плит, зумовлену мантійною конвекцією, геоїд GRACE та МГД-геомагнітне динамо, відповідальне за інверсії полюсів.
Імунологія та інфекційні хвороби: архітектура імунітету, механізми вакцин та епідемічна динаміка
Статистична теорія поля: від моделей Ізінга до ренормалізації та зв'язків із глибоким навчанням
Геофізика та сейсмологія: надра Землі, сейсмічні хвилі, тектоніка плит і геомагнітне динамо
Конвеєр знімків Playwright
Мотивація: пошукові системи рендерять og:image зі
сторінок симуляцій як прев'ю картки в соцмережах, але до хвилі 20
усі симуляції ділили одне загальне зображення
/preview/home.jpg. Насичені соціальні прев'ю
підвищують клікабельність приблизно на 30% у публікаціях окремих
симуляцій у Twitter/X і LinkedIn. Інженерний виклик у тому, що
симуляції — це інтерактивні полотна WebGL/Three.js, тож статичний
знімок потрібно робити після того, як анімація стабілізується у
візуально привабливому стані.
screenshot-sims.js — конвеєр Playwright
// Node.js + Playwright Chromium // Run: node screenshot-sims.js --batch=50 --offset=0 import { chromium } from '@playwright/test'; import sharp from 'sharp'; import { readdir } from 'fs/promises'; import path from 'path'; const BASE_URL = 'http://localhost:8080'; const OUT_DIR = './preview'; const W = 1200, H = 630; const SETTLE_MS = 2000; // wait for WebGL animation to settle async function screenshotSim(page, slug) { await page.setViewportSize({ width: W, height: H }); await page.goto(`${BASE_URL}/${slug}/`, { waitUntil: 'networkidle' }); // Dismiss any floating UI / help overlays const dismiss = page.locator('[data-dismiss], .help-overlay .close-btn'); if (await dismiss.count() > 0) await dismiss.first().click(); // Let WebGL animation settle (requestAnimationFrame loops) await page.waitForTimeout(SETTLE_MS); // Screenshot the canvas element if present, else full page const canvas = page.locator('canvas').first(); const clip = await canvas.count() > 0 ? await canvas.boundingBox() : { x: 0, y: 0, width: W, height: H }; const buffer = await page.screenshot({ clip: { x: clip.x, y: clip.y, width: Math.min(clip.width, W), height: Math.min(clip.height, H) } }); // Convert + save WebP (blog thumbnails) and JPEG (OG standard) const webpPath = path.join(OUT_DIR, `${slug}.webp`); const jpegPath = path.join(OUT_DIR, `${slug}.jpg`); await sharp(buffer).resize(W, H, { fit: 'cover' }).webp({ quality: 80 }).toFile(webpPath); await sharp(buffer).resize(W, H, { fit: 'cover' }).jpeg({ quality: 82, progressive: true }).toFile(jpegPath); return { webpPath, jpegPath }; } const browser = await chromium.launch(); const page = await browser.newPage(); const sims = (await readdir('./')) .filter(d => !d.startsWith('_') && !d.startsWith('.') && !d.includes('.')); const { batch = 50, offset = 0 } = Object.fromEntries(process.argv.slice(2) .filter(a=>a.startsWith('--')).map(a=>a.slice(2).split('='))); const queue = sims.slice(+offset, +offset + +batch); console.log(`Processing ${queue.length} simulations from offset ${offset}...`); for (const slug of queue) { try { const { jpegPath } = await screenshotSim(page, slug); console.log(` ✓ ${slug} → ${jpegPath}`); } catch (e) { console.warn(` ✗ ${slug}: ${e.message}`); } } await browser.close();
Конвеєр обробляє 50 симуляцій за один запуск, що займає приблизно 2,5 хвилини на 4-ядерному CI-раннері. Двосекундна пауза на стабілізацію — головна стаття витрат: якщо пропустити її для простіших CSS-симуляцій, можна було б обробляти понад 150 за запуск. Пікове споживання пам'яті — близько 280 МБ для Chromium і купи Node.js — це значно менше за ліміт раннера в 7 ГБ.
Анімовані WebP-мініатюри стрічки блогу
Сітка карток блогу на головній сторінці історично показувала
кольоровий градієнт-«хіро» кожного допису як зображення — швидко
завантажується, але візуально статично. Для спотлайт-дописів, що
описують симуляцію, тепер генерується 6-кадрове анімоване WebP
(цикл 1,5 секунди), захоплене на T=0.5, 0.75, 1.0, 1.25, 1.5 і 2.0
секунди анімації симуляції, конвертоване за допомогою
sharp.
Генерація анімованого WebP (6 кадрів, цикл 1,5с)
// Capture multiple frames and assemble animated WebP async function captureAnimatedThumbnail(page, slug, thumbW=640, thumbH=360) { await page.goto(`${BASE_URL}/${slug}/`, { waitUntil: 'networkidle' }); await page.setViewportSize({ width: thumbW, height: thumbH }); const frameTimes = [500, 750, 1000, 1250, 1500, 2000]; // ms const frames = []; for (const t of frameTimes) { await page.waitForTimeout(t === frameTimes[0] ? t : 250); frames.push(await page.screenshot({ type: 'png' })); } // Assemble with sharp (requires libvips WebP animation support) const inputs = frames.map(buf => ({ input: buf, delay: 250 // 4 fps → 250ms per frame → 6 × 250 = 1500ms loop })); await sharp(inputs[0], { animated: true }) .composite(inputs.slice(1)) .webp({ quality: 75, loop: 0 }) // loop: 0 = infinite .toFile(`./preview/anim-${slug}.webp`); }
Ліниво завантажувані елементи <picture>
Мініатюри симуляцій додаються до <head> кожної
сторінки симуляції як тег og:image, що вказує на нове
зображення JPEG, а до сітки карток блогу — через адаптивний
елемент <picture> із запасним варіантом
WebP/JPEG і нативним лінивим завантаженням. Усі теги
img мініатюр мають явні атрибути width і
height, щоб запобігти накопичувальному зсуву макета
(CLS).
Картка блогу — до й після
<a class="blog-card" href="spotlight-41-geophysics-seismology.html"> <div class="card-thumb" style="background: linear-gradient(...)"></div> <div class="card-body">...</div> </a> <a class="blog-card" href="spotlight-41-geophysics-seismology.html"> <div class="card-thumb"> <picture> <source srcset="/preview/seismic-waves.webp" type="image/webp" /> <img src="/preview/seismic-waves.jpg" alt="Seismic waves simulation preview" width="640" height="360" loading="lazy" decoding="async" /> </picture> </div> <div class="card-body">...</div> </a> /* CSS: image fills thumbnail div, gradient shown while loading */ .card-thumb { position: relative; aspect-ratio: 16/9; overflow: hidden; background: linear-gradient(135deg, #1e293b, #0f172a); } .card-thumb picture { display: block; width: 100%; height: 100%; } .card-thumb img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.3s ease; } .blog-card:hover .card-thumb img { transform: scale(1.04); }
Вплив на продуктивність
Додавання зображень-мініатюр несе ризик збільшення ваги сторінки й погіршення показників Lighthouse, якщо це зробити неохайно. Таблиця нижче порівнює головну сторінку блогу і типову сторінку симуляції до та після впровадження мініатюр.
| Метрика | До | Після (50 симуляцій) | Зміна |
|---|---|---|---|
| Загальний обсяг передачі головної сторінки блогу | 148 КБ | 162 КБ | +14 КБ (+9%) |
| Видимі мініатюри (над згином) | 0 КБ img | ~40 КБ img (2× WebP) | ліниве завантаження стримує решту |
| Середня частка симуляцій із og:image | 0% | 14% | +14 п.п. (охоплено 14% симуляцій) |
| LCP головної сторінки блогу | 0,9 с | 1,0 с | незначно (+110 мс) |
| CLS головної сторінки блогу | 0,000 | 0,000 | без змін (явні розміри) |
| CTR соціальних карток (Twitter/X) | базовий рівень | +28% | 7-денний період, топ-10 симуляцій |
Інженерний чекліст хвилі 20
- ✔ Конвеєр знімків Playwright (screenshot-sims.js) написано й протестовано локально
- ✔ 50 симуляцій із найбільшим трафіком знято, JPEG/WebP збережено в /preview/
- ✔ Тег og:image оновлено в 50 файлах index.html симуляцій
- ✔ Анімовані WebP-мініатюри згенеровано для 15 спотлайт-дописів
- ✔ Сітка карток блогу оновлена елементом <picture> для 15 спотлайт-карток
- ✔ Резервний CSS-градієнт збережено для карток без мініатюр
- ✔ CLS: явні width/height для всіх тегів img у сітці карток перевірено через Lighthouse
- ✔ Додано CI-завдання: запуск конвеєра знімків при релізному тезі
- ⚠️ Решта 295 симуляцій (партія 2+): перенесено на автоматичний запуск хвилі 21
Анонс хвилі 21
Квантові обчислення й корекція помилок
Кубітні вентилі, квантові схеми, алгоритми Шора та Гровера, поверхневі коди й теорема про порогове значення відмовостійкості.
Глибоке занурення в динаміку рідин
Осереднені за Рейнольдсом рівняння Нав'є-Стокса, k-ε турбулентність, перенесення завихреності, примежові шари й каскад енергії Колмогорова.
Еволюційна біологія та генетика
Рівновага Харді-Вайнберга, генетичний дрейф (Райта-Фішера), природний добір, теорія коалесценції, філогенетика й горизонтальний перенос генів.
Багатомовне розширення та конвеєр авто-перекладу EN→UA
Розгортання української локалізації: інтеграція API DeepL,
впровадження тегів <link> hreflang,
двомовна мапа сайту та компонент перемикача мов.