2026-04-16 23:00:25 +02:00
2026-04-16 23:00:25 +02:00
2026-04-16 23:00:25 +02:00
2026-04-16 23:00:25 +02:00

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:

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.
  • themeblue / 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:

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:

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:

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):

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:

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):

{
  "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ć
S
Description
No description provided
Readme
49 KiB
Languages
HTML 100%