Statystyki platformy po Fali 20
Fala kamieni milowych: Fala 20 to 20. fala treści platformy, 30. wpis Learning (Learning #30 — Statystyczna teoria pola) i 40. Devlog. Seria Learning objęła już mechanikę klasyczną, elektrodynamikę, mechanikę kwantową, szczególną i ogólną teorię względności, termodynamikę, teorię chaosu i statystyczną teorię pola od podstaw.
Podsumowanie Fali 20
Fala 20 otworzyła granicę biologii i fizyki. Spotlight #40 zmapował układ odpornościowy — od receptorów Toll-podobnych i kaskady dopełniacza przez rekombinację V(D)J limfocytów T, dojrzewanie powinowactwa przeciwciał i szczepionki mRNA po dynamikę epidemii SEIR i immunoterapię punktów kontrolnych. Learning #30 rozwinął statystyczną teorię pola od kontinualnej granicy Landaua-Ginzburga modelu Isinga przez całki po trajektoriach Feynmana, grupę renormalizacji Wilsona, klasy uniwersalności i dwuwymiarową CFT, kończąc na głębokich powiązaniach z modelami energetycznymi uczenia maszynowego i generatywnymi modelami dyfuzyjnymi. Spotlight #41 zmapował wnętrze Ziemi — warstwy PREM, czasy przebiegu fal sejsmicznych i strefy cienia, momentową magnitudę, tektonikę płyt napędzaną konwekcją płaszcza, geoidę GRACE oraz magnetohydrodynamiczne dynamo geomagnetyczne odpowiedzialne za odwrócenia biegunów.
Immunologia i choroby zakaźne: architektura odporności, mechanizmy szczepionek i dynamika epidemii
Statystyczna teoria pola: od modeli Isinga do renormalizacji i powiązań z głębokim uczeniem
Geofizyka i sejsmologia: wnętrze Ziemi, fale sejsmiczne, tektonika płyt i dynamo geomagnetyczne
Potok zrzutów ekranu Playwright
Motywacja: wyszukiwarki renderują og:image ze stron
symulacji jako podgląd karty społecznościowej, ale przed Falą 20
każda symulacja dzieliła ten sam ogólny obraz
/preview/home.jpg. Bogate podglądy społecznościowe
zwiększają współczynnik klikalności o ~30% w udostępnieniach
poszczególnych symulacji na Twitterze/X i LinkedIn. Wyzwanie
inżynierskie polega na tym, że symulacje to interaktywne
płótna WebGL/Three.js — statyczny zrzut ekranu musi zostać
wykonany po ustabilizowaniu się animacji w wizualnie atrakcyjnym
stanie.
screenshot-sims.js — potok Playwright
// Node.js + Playwright Chromium // Uruchomienie: 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; // czekaj, aż animacja WebGL się ustabilizuje async function screenshotSim(page, slug) { await page.setViewportSize({ width: W, height: H }); await page.goto(`${BASE_URL}/${slug}/`, { waitUntil: 'networkidle' }); // Zamknij ewentualne pływające UI / nakładki pomocy const dismiss = page.locator('[data-dismiss], .help-overlay .close-btn'); if (await dismiss.count() > 0) await dismiss.first().click(); // Poczekaj, aż animacja WebGL się ustabilizuje (pętle requestAnimationFrame) await page.waitForTimeout(SETTLE_MS); // Zrzut ekranu elementu canvas, jeśli istnieje, w przeciwnym razie całej strony 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) } }); // Konwersja + zapis WebP (miniatury bloga) i JPEG (standard OG) 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(`Przetwarzanie ${queue.length} symulacji od offsetu ${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();
Potok przetwarza 50 symulacji na uruchomienie, co zajmuje około 2,5 minuty na 4-rdzeniowym runnerze CI. Dominującym kosztem jest 2-sekundowy czas ustabilizowania — pominięcie go w prostszych symulacjach opartych wyłącznie na CSS pozwoliłoby przetworzyć 150+ na uruchomienie. Szczytowe zużycie pamięci wynosi ~280 MB dla Chromium + stosu Node; z dużym zapasem w limicie 7 GB runnera.
Animowane miniatury WebP dla kanału bloga
Siatka kart bloga na stronie indeksu historycznie pokazywała
kolorowy gradient bohatera każdego wpisu jako element wizualny —
szybki do załadowania, ale statyczny wizualnie. Dla wpisów
Spotlight opisujących symulację generujemy teraz 6-klatkowy
animowany WebP (pętla 1,5 s) przechwycony w chwilach T=0,5,
0,75, 1,0, 1,25, 1,5 i 2,0 s animacji symulacji, konwertowany
za pomocą sharp.
Generowanie animowanego WebP (6 klatek, pętla 1,5 s)
// Przechwycenie wielu klatek i złożenie animowanego 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' })); } // Złożenie za pomocą sharp (wymaga obsługi animacji WebP w libvips) const inputs = frames.map(buf => ({ input: buf, delay: 250 // 4 fps → 250ms na klatkę → 6 × 250 = 1500ms pętli })); await sharp(inputs[0], { animated: true }) .composite(inputs.slice(1)) .webp({ quality: 75, loop: 0 }) // loop: 0 = nieskończona .toFile(`./preview/anim-${slug}.webp`); }
Leniwie ładowane elementy <picture>
Miniatury symulacji dodawane są do <head>
każdej strony symulacji jako znacznik meta og:image
wskazujący na nowy JPEG oraz do siatki kart bloga poprzez
responsywny element <picture> z fallbackiem
WebP/JPEG i natywnym leniwym ładowaniem. Wszystkie znaczniki
img miniatur mają jawne atrybuty width
i height, aby zapobiec skumulowanemu przesunięciu
layoutu (CLS).
Karta bloga — przed i po
<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="Podgląd symulacji fal sejsmicznych" width="640" height="360" loading="lazy" decoding="async" /> </picture> </div> <div class="card-body">...</div> </a> /* CSS: obraz wypełnia div miniatury, gradient widoczny podczas ładowania */ .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); }
Wpływ na wydajność
Dodanie obrazów miniatur niesie ryzyko zwiększenia wagi strony i pogorszenia wyników Lighthouse, jeśli nie zostanie odpowiednio obsłużone. Poniższa tabela porównuje indeks bloga i reprezentatywną stronę symulacji przed i po wdrożeniu miniatur.
| Metryka | Przed | Po (50 symulacji) | Zmiana |
|---|---|---|---|
| Łączny transfer indeksu bloga | 148 KB | 162 KB | +14 KB (+9%) |
| Widoczne miniatury (powyżej zakrętu) | 0 KB obrazu | ~40 KB obrazu (2× WebP) | leniwe ładowanie zapobiega reszcie |
| Średni wskaźnik trafień og:image symulacji | 0% | 14% | +14 pp (14% symulacji objęte) |
| LCP indeksu bloga | 0,9 s | 1,0 s | pomijalne (+110 ms) |
| CLS indeksu bloga | 0,000 | 0,000 | bez zmian (jawne wymiary) |
| CTR karty społecznościowej (Twitter/X) | wartość bazowa | +28% | 7-dniowy przegląd, top 10 symulacji |
Lista kontrolna inżynierska Fali 20
- ✔ Potok zrzutów ekranu Playwright (screenshot-sims.js) napisany i przetestowany lokalnie
- ✔ 50 symulacji o najwyższym ruchu sfotografowanych i zapisanych JPEG/WebP w /preview/
- ✔ Znacznik meta og:image zaktualizowany w 50 plikach index.html symulacji
- ✔ Animowane miniatury WebP wygenerowane dla 15 wpisów Spotlight
- ✔ Siatka kart bloga zaktualizowana o element <picture> dla 15 kart spotlight
- ✔ Fallback gradientu CSS zachowany dla kart bez miniatury
- ✔ CLS: jawne width/height na wszystkich znacznikach img w siatce kart zweryfikowane przez Lighthouse
- ✔ Dodano zadanie CI: uruchamia potok zrzutów ekranu przy tagu wydania
- ⚠️ Pozostałe 295 symulacji (partia 2+): odłożone na automatyczne uruchomienie Fali 21
Zapowiedź Fali 21
Obliczenia kwantowe i korekcja błędów
Bramki kubitowe, obwody kwantowe, algorytmy Shora i Grovera, kody powierzchniowe i twierdzenie o progu odporności na błędy.
Głębokie zanurzenie w dynamikę płynów
Równania Naviera-Stokesa uśrednione Reynoldsem, turbulencja k-ε, transport wirowości, warstwy przyścienne i kaskada energii Kołmogorowa.
Biologia ewolucyjna i genetyka
Równowaga Hardy'ego-Weinberga, dryf genetyczny (Wright-Fisher), dobór naturalny, teoria koalescencji, filogenetyka i horyzontalny transfer genów.
Ekspansja wielojęzyczna i potok automatycznego tłumaczenia EN→UA
Wdrożenie i18n dla ukraińskiego: integracja API DeepL, wstrzykiwanie znaczników <link> hreflang, dwujęzyczna mapa witryny i komponent przełącznika języka.