Dla nerdów
Specyfikacja strony, na której jesteś. Bez tłumaczenia na ludzki, od tego są pozostałe podstrony.
Stos
- serwer
php:8.4-apachew Dockerze, Coolify na Hetznerze; brotli, potem gzip- aplikacja
- PHP bez frameworka, bez bazy danych, bez kroku budowania; wspólne części przez
require, treść zmienna wdane/*.json - adresy
mod_rewrite:/cennik→cennik.php; mapa strony generowana z plików, które naprawdę istnieją- 3D
- Three.js 0.186.0, odchudzony esbuildem do tego, czego używa znak (
three-znak.min.js, 539 KB, po brotli 127 KB) - kroje
- Jost (zmienny, 100–900) i Martian Mono,
woff2z własnego serwera, przycięte do potrzebnych znaków - zewnętrzne
- zero CDN: kroje i biblioteki z tej samej domeny; CDN oddaje IP odwiedzającego komuś trzeciemu. Jedyny obcy skrypt to Google Tag Manager z pomiarem odwiedzin, a wczytuje się dopiero po zgodzie z banera; bez niej strona nie łączy się z Google
- poczta
- formularz →
api/kontakt.php→ Brevo API; token jednorazowy, pułapka czasowa 3 s, limit 3 / 10 min i 8 / h z adresu
// WordPress? Postawić CMS, a potem wyciąć z niego wszystko, co daje. Już to przerabiałem.
Dlaczego nie WordPress
Treść prowadzi jedna osoba, bez ról i redakcji w panelu. Strona czyta sześć małych plików JSON: realizacje, narzędzia Laboratorium, wycenę z cenami rynkowymi, dane kontaktowe, wyniki PageSpeed do /outsourcing i liczby z tej podstrony. Cały ciężar leży w WebGL, ruchu i preloadzie, a tego żaden motyw ani wtyczka nie daje. WordPress dokładałby bazę, aktualizacje i powierzchnię ataku, nie dając w zamian niczego, czego ta strona używa.
Znak nieskończoności
- kontekst
- WebGL 2 (GLSL ES 3.00), jedna scena na stronę: galaktyka, znak, pył i poświata w jednym przebiegu
- bryła
- parametryczna rura po osi lemniskaty (długość łuku
u× kąt przekroju), złącze zlewane wygładzonym minimum; od 31 411 (poziom 1) do 182 115 (poziom 5) wierzchołków; faktura farby i włosia pędzla wypieczona w geometrii, liczona w Web Workerze i trzymana w IndexedDB - pył
- symulacja na GPU (
GPUComputationRenderer): pozycja i prędkość w teksturach; 21 904 punkty na poziomach 1–3 i 54 756 na 5; do tego 170 630 wierzchołków włókien na poziomie 5 - rozpad
- globalny (przewijanie): bryła i cząstki liczą tę samą funkcję
rozpadG(pozycja); lokalny (kursor): mapa ubytkuu × kątna GPU, 60% wyrwanego pyłu wraca i odbudowuje bryłę - barwy
- tablica 2049 × 2 (barwa i blask w poprzek znaku) w teksturze; pasma światła, włosie i lakier w cieniowaniu; porwanie prawego górnego łuku jako strefa z kawałkami (Voronoi z szarpaniem)
- jakość
- cztery poziomy (1, 2, 3 i 5), drabina z histerezą przy pierwszym wejściu, wynik zapamiętany: w górę 1→2 od 40 kl/s, 2→3 od 40, 3→5 od 57 (tylko komputer; na dotyku 3 jest sufitem); w dół 2→1 poniżej 30, 3→2 poniżej 35, 5→3 poniżej 50
- ostrość
- nigdy poniżej 1 piksela obrazu na piksel CSS; telefon rozciąga najwyżej ~1,35×, tablet i komputer ~2,25×; sufit klatek 60 (dotyk) i 120 (komputer)
// Poniżej poziomu 1 nie schodzimy. Słabszy telefon dostaje mniej klatek, nie brzydszy znak.
Preload
- zasada
- treść jest w HTML od pierwszego bajtu, preload to nakładka nad gotowym dokumentem, nie blokada renderowania; bez JS nakładki nie ma
- wejście
- okno rysuje się kreską → tło z cienia (0,8 s) → pył wyłania się (1,2 s) → składa w bryłę (1,7 s) → okno się rozchodzi, znak dojeżdża z pełnym obrotem (1,3 s)
- przejście
- klik → znak leci do okna ze szkieletem strony docelowej (1,15 s) → rozpyla się (1,4 s), niebo gaśnie (2,2 s) → szew w czerni → przeładowanie; faza pętli okna przechodzi przez szew w
sessionStorage, więc okno nie restartuje - awaryjnie
- ostrzeżenie po 5 s od wstępu, twardy bezpiecznik 12 s, błąd sceny: wyjście od razu; „pomiń" zawsze pod ręką
- rodziny
- osobna rodzina przy przejściach wewnątrz Laboratorium: bez okna i szkieletu; znak rośnie ku ekranowi (bryła ~2,6 szerokości ekranu) i rozpyla się na całą treść, bez znaku gaśnie sama nakładka → 0,65 s czerni → treść wchodzi kaskadą, a znak (jeśli podstrona go ma) składa się z pyłu od razu na swoim miejscu
Ruch
- tor znaku
- 1:1 z przewijaniem: kotwice w znaczniku (
data-znak-poza="x:.56 y:.34 z:-1 s:.4"), między nimi Hermite ze stycznymi monotonicznymi, prędkość ciągła, bez przestrzeliwania - przewijanie
- kółko z własną bezwładnością (
przewijanie.js), dotyk systemowy; ruch sekcji:scrollcraft, bez GSAP - zatrzask
- oskryptowane przejście 2,4 s, przeskok strony w 45% animacji; próg celowo nieczuły: 150 px kółka, 60 px palca; dogrywa do końca i ignoruje gesty
- reduced motion
prefers-reduced-motion: bez preloadu, znak stoi w pozie najbliższej sekcji, zatrzask przeskakuje bez animacji, dryf i fale gasną
Laboratorium
- podgląd
iframezsandboxbezallow-top-navigation; blokady ramki rodzic nie widzi (load ~220 ms i te same wyjątki co przy stronie, która się wczytała), więc heurystyka: lista domen i czas- studio
- żywy znak strony +
fwScena.studio(): uniform strefy porwania, rozpad, widoczność warstw, przepisana tablica barw, tempo światła - grafika
- WebP / PNG / JPEG przez
canvas.toBlob; AVIF:@jsquash/avif(libavif w WASM, 3,4 MB) w osobnym wątku, dociągany przy pierwszym użyciu - QR
- koder
uqrw przeglądarce; eksport PNG, SVG, JPG, WebP i PDF bez udziału serwera - waluty
- średnie kursy NBP (tabela A) pobiera serwer i trzyma kopię; przeglądarka pyta tylko
/api/kursy, przeliczenie i wykres idą lokalnie
Podłoga techniczna
- nagłówki
- Strict-Transport-Security, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Cross-Origin-Opener-Policy, Permissions-Policy, Content-Security-Policy (główna i dwa warianty dla narzędzi Laboratorium)
- CSP
default-src 'self'; wyjątki per narzędzie w<If>:frame-src https:dla podglądu,'wasm-unsafe-eval'dla AVIF- dane strukturalne
- ProfessionalService, WebSite, WebPage, Person, AboutPage, CollectionPage, ItemList, Service, AggregateOffer, ContactPage, CreativeWork, WebApplication, BreadcrumbList, TechArticle
- indeksacja
/sitemap.xmlz plików, które istnieją;robots.txt; kanoniczne adresy i Open Graph na każdej podstronie- pamięć
- kod i style z
ETagino-cache(304 bez treści), bez wersji w nazwach plików
Liczby
| strona główna: przesłane (brotli) / po rozpakowaniu | 749 KB / 1,61 MB |
|---|---|
| z tego JavaScript (po rozpakowaniu) | 1,01 MB w 29 plikach (297 KB przesłane) |
| zapytania przy pierwszym wejściu | 49 unikalnych adresów |
| pliki w obrazie, razem 45,0 MB (PHP / JS / CSS / obrazy / kroje) | 241: 47 / 53 / 24 / 90 / 6 (+ 21 innych: dane, konfiguracja, WASM) |
| wynik Lighthouse z 6.10.2026: mobilny / komputer | 67 / 71 |
| LCP / CLS / TBT (mobilny, symulowane) | 1,8 s / 0 / 4,7 s |
Pomiar: strona główna na futureweb.com.pl, pierwsze wejście z pustą pamięcią podręczną, Lighthouse 13.5.0 w Chrome 154 (symulowane dławienie, preset mobilny i komputerowy, po jednym biegu), z komputera, nie z telefonu; „przesłane” to bajty z sieci po kompresji brotli, 6.10.2026. Pliki: zawartość obrazu Dockera, czyli repozytorium bez wykluczeń z .dockerignore (bez Dockerfile, plików gita, pełnej paczki three.js i prototypu znaku); obrazy to WebP, PNG, JPG, SVG i ICO; inne to dane JSON, .htaccess, robots.txt, manifest, koder WASM, licencje i strona błędu 500, 1.11.2026.
Narzędzia i sprzęt
- języki
- PHP, JavaScript i TypeScript, CSS, Python
- frameworki
- React i inne; ta strona nie używa żadnego
- CMS
- WordPress, motyw pisany od zera
- serwery
- Docker; własny hosting na Hetznerze z Coolify (tu stoi ta strona) i SEOhost dla stron klientów
- 3D
- Blender
- komputer
- Ryzen 5 5600, Radeon RX 6600 XT, 16 GB RAM, płyta MSI, chłodzenie powietrzem
- na biurku
- monitor Acer, klawiatura Krux
// Chłodzenie powietrzem. Woda zostaje w szklance.
Dlaczego ta strona nie ma 90 w PageSpeed
W cenniku obiecuję klientom 90+. Ta strona tego wyniku nie osiąga i to jest świadomy wybór.
Strony, które robię dla klientów, nie mają sceny 3D, symulacji pyłu na karcie graficznej ani preloadu z przejściami, tam 90+ jest realne i sprawdzalne. Ta strona jest moją wizytówką, więc ma pokazać, co da się zrobić w przeglądarce. Motto projektu: jakość ponad optymalizację.
Wynik z tabeli wyżej (67 / 71) to Lighthouse na produkcji, nie moje szacunki. LCP to nie wolny serwer: za największy element Lighthouse bierze logo w nakładce preloadu i liczy intro jak ładowanie strony.
Najwięcej punktów kosztuje: wykonanie JavaScriptu sceny przy pierwszym wejściu, Three.js w paczce i praca karty graficznej, której Lighthouse nie odróżnia od zablokowanego wątku. Co zrobiłem, żeby kosztowało mniej: treść w HTML od pierwszej sekundy, preload jako nakładka, bryła liczona w tle i trzymana w IndexedDB, poziomy jakości dobrane pomiarem na urządzeniu, scena rysuje tylko to, co widać.
Zmierz sam w PageSpeed Insights (otwiera się w nowej karcie)
// Wolałem to napisać sam, niż czekać, aż ktoś zmierzy i zapyta.