# trakodag Edytor obrazu sterowany kodem. Każda warstwa to osobny program w JavaScripcie, który operuje na pikselach wynikowego obrazu poprzedniej warstwy. Pełny dostęp do każdego piksela, pętle, zmienne, funkcje matematyczne, dodatkowe obrazy jako tablice pikseli. Cała apka to jeden plik `index.html` — bez zależności, bez build-stepu. ## Uruchomienie Otwórz `index.html` w przeglądarce: ```sh xdg-open index.html # Linux open index.html # macOS firefox index.html # albo dowolna inna przeglądarka ``` ## Model warstw Warstwy tworzą **pipeline**: ``` original → layer 1 → layer 2 → … → final ``` Każda warstwa otrzymuje wynik poprzedniej jako `prev` (dla pierwszej `prev = original`) i pisze do `output`. Wynik trafia jako `prev` do następnej warstwy. Suwak **strength** każdej warstwy to mieszanie z wejściem: `lerp(prev, output, strength)` — przy `0.00` warstwa jest niewidoczna, przy `1.00` w pełni nadpisuje. Wyłączenie ikony oka pomija warstwę całkowicie. ## Interfejs - **Load image** — załaduj obraz źródłowy (PNG, JPG, …) - **+ Add image** — dorzuć dodatkowy obraz dostępny w kodzie jako `images[i]` - **Run / auto-run** — odpalenie pipeline'u (auto-run debouncuje wpisywanie ~250 ms); skrót `Ctrl/Cmd + Enter` - **scale** — rozdzielczość robocza podglądu (`1x`, `0.5x`, `0.25x`, `0.125x`). Per-piksel JS jest wolny, więc edytuj na np. `0.25x`, eksport zawsze leci w `1x`. - **theme** — `blue / orange` lub `purple / sea`; wybór pamiętany w `localStorage` - **Export PNG** — render w pełnej rozdzielczości i pobranie pliku - **Save project / Load project** — `.json` z samymi warstwami i kodem (bez obrazu) - **? / ⓘ Vars** — ściąga API i pełny zestaw przykładów Najechanie kursorem na canvas pokazuje **oryginał**. ## Edytor - Kolorowanie składni: komentarze, stringi, liczby, słowa kluczowe, nazwy API, funkcje, operatory. - `Tab` wstawia 2 spacje. - Nazwy warstw: dwuklik → edycja inline. ## Code API Zmienne dostępne w kodzie warstwy: | Nazwa | Co to | |---|---| | `width`, `height` | wymiary obrazu w aktualnej rozdzielczości roboczej | | `original` | obiekt źródłowego obrazu: `{ pixels, width, height, getPixel(x,y) }` | | `prev` | wynik poprzedniej warstwy (dla pierwszej = `original`), ten sam kształt | | `images[i]` | dodatkowe obrazy dorzucone przyciskiem **+ Add image** | | `output` | `Uint8ClampedArray` RGBA długości `width*height*4` — to do tego piszesz | Helpery: | Nazwa | Działanie | |---|---| | `getPixel(x, y)` | skrót do `prev.getPixel(x, y)` → `{r, g, b, a}` | | `setPixel(x, y, r, g, b, a)` | zapis piksela; `a` domyślnie `255` | | `setPixel(x, y, p)` | zapis z obiektu `{r, g, b, a}` | | `forEach((x, y, p) => { … })` | iteracja po wszystkich pikselach `prev`; mutuj `p` lub zwróć nowy | | `clamp(v, lo=0, hi=255)` | obcięcie do zakresu | | `lerp(a, b, t)` | interpolacja liniowa | | `log(...)` | wypisanie do panelu konsoli pod edytorem | Random: | Nazwa | Wynik | |---|---| | `random1()` | float `[0, 1)` | | `random256()` | int `[0, 255]` | | `randomBoolean()` | `true` / `false` (50/50) | | `randomRange(lo, hi)` | float `[lo, hi)` | | `randomInt(lo, hi)` | int `[lo, hi]` (inclusive) | | `random()` | alias `Math.random()` | Matematyka — wszystko z `Math` jest dostępne pod skróconymi nazwami: `sin`, `cos`, `tan`, `asin`, `acos`, `atan`, `atan2`, `abs`, `sqrt`, `pow`, `exp`, `floor`, `ceil`, `round`, `min`, `max`, `hypot`, `sign`, plus stałe `PI`, `TAU`, `E`. Pełen obiekt też jest pod `Math`. ## Przykłady Brightness +5: ```js forEach((x, y, p) => { p.r = clamp(p.r + 5); p.g = clamp(p.g + 5); p.b = clamp(p.b + 5); }); ``` Maksymalny niebieski w kolumnie x = 50: ```js forEach((x, y, p) => { if (x === 50) { p.r = 0; p.g = 0; p.b = 255; } }); ``` Co 500-ny piksel: pomnóż zielony kanał razy 2: ```js forEach((x, y, p) => { const i = y * width + x; if (i % 500 === 0) p.g = clamp(p.g * 2); }); ``` Wklejenie obrazu `images[0]` w punkcie `(50, 100)`: ```js const img = images[0]; const offsetX = 50, offsetY = 100; for (let y = 0; y < img.height; y++) { for (let x = 0; x < img.width; x++) { const dx = x + offsetX, dy = y + offsetY; if (dx < 0 || dx >= width || dy < 0 || dy >= height) continue; const q = img.getPixel(x, y); setPixel(dx, dy, q.r, q.g, q.b, q.a); } } ``` Sinusoidalne przesunięcie poziome: ```js for (let y = 0; y < height; y++) { for (let x = 0; x < width; x++) { const dx = floor(sin(y * 0.05) * 20); const p = prev.getPixel((x + dx + width) % width, y); setPixel(x, y, p.r, p.g, p.b, p.a); } } ``` Więcej przykładów w panelu `?` — basics / coordinates / random / geometry / second image. ## Format pliku projektu `Save project` zapisuje JSON ze stanem warstw (sam kod, bez obrazu): ```json { "format": "trakodag.web", "version": 1, "savedAt": "2026-04-16T20:00:00.000Z", "layers": [ { "name": "base", "code": "forEach((x,y,p) => { p.r = clamp(p.r + 5); ... });", "visible": true, "opacity": 1.0 } ] } ``` Obraz wczytujesz osobno przyciskiem **Load image** — projekt jest przenośny między różnymi obrazami. ## Wydajność Per-piksel JS na obrazach kilkudziesięciu MP będzie zauważalnie wolny. Schemat pracy: 1. Wczytaj obraz, zostaw `scale` na `0.25x` (lub `0.125x` dla bardzo dużych) 2. Pisz i debuguj kod na małym podglądzie 3. Przed eksportem opcjonalnie podbij `scale` żeby zobaczyć finalny render 4. **Export PNG** zawsze leci w `1x` — nie musisz nic zmieniać