dgm.js — nieskończone płótno i inteligentne kształty w Reakcie
Biblioteka nieskończonego płótna z bezgłowymi komponentami dla Reacta, wielostronicowością, współpracą w czasie rzeczywistym i eksportem do PDF — a przy tym z kształtami skryptowanymi we wbudowanym Lispie. Rzecz, która decyduje o wdrożeniu, jest jednak licencyjna: GPL-3.0 przy pakiecie wysyłanym do przeglądarki oznacza coś innego niż GPL na serwerze. Wyjaśniamy co i pokazujemy zweryfikowane alternatywy.
Prośba „chcielibyśmy mieć w panelu tablicę do rysowania" pojawia się w projektach częściej, niż mogłoby się wydawać — przy planowaniu przestrzeni, schematach procesów, mapach zależności między obiektami albo prostym narzędziu do adnotacji. Nieskończone płótno z kształtami, które łączą się liniami i pamiętają połączenia, jest sporym kawałkiem inżynierii i nikt nie pisze go od zera.
dgm.js (github.com/dgmjs/dgmjs) jest jedną z odpowiedzi na tę potrzebę i technicznie odpowiedzią ciekawą — z bezgłowymi komponentami dla Reacta, wielostronicowością, współpracą w czasie rzeczywistym, eksportem do PDF i kształtami, które da się skryptować we wbudowanym dialekcie Lispa.
Ten wpis odwraca jednak zwykłą kolejność i licencję omawia nie na końcu, a jako rzecz najważniejszą. Powód jest prosty: przy bibliotece frontendowej na GPL-3.0 licencja rozstrzyga o wdrożeniu, zanim dojdzie się do oceny funkcji — i rozstrzyga inaczej, niż podpowiada intuicja wyrobiona na oprogramowaniu serwerowym.
Stan projektu
Dane z API GitHuba i npm na 7 października 2026:
- 1671 gwiazdek i 85 forków, repozytorium założone 4 lutego 2024, kod w TypeScripcie,
- licencja GPL-3.0 z osobną opcją komercyjną,
- ostatnia wersja 1.5.5 opublikowana 26 grudnia 2025 — i tego samego dnia był ostatni push do repozytorium, czyli ponad dziewięć miesięcy ciszy,
- 20 otwartych zgłoszeń oraz — liczba, która mówi najwięcej o skali użycia — 521 pobrań pakietu z npm w ostatnim miesiącu,
- pięć pakietów:
core,react,export,pdfi wtyczka do Yjs na współpracę w czasie rzeczywistym.
Dwie rzeczy w organizacji tego projektu trzeba powiedzieć wprost, bo obie wpływają na decyzję o użyciu. Pierwsza jest zapisana w README bez ogródek: projekt nie przyjmuje wkładu z zewnątrz i nie akceptuje żadnych pull requestów. Nie jest to więc projekt społecznościowy, a otwarty kod produktu rozwijanego przez jedną osobę.
Druga wynika z listy wdrożeń w README: wymienione tam Frame0 (narzędzie do szkicowania makiet) i DGM App to produkty tego samego autora. Biblioteka jest więc silnikiem komercyjnych aplikacji, udostępnionym na copyleftowej licencji ze ścieżką odpłatną — model zupełnie legalny i uczciwie opisany, ale zmieniający sposób, w jaki należy patrzeć na jej rozwój. Dziewięć miesięcy bez commita nie znaczy tu porzucenia, bo autor ma biznesowy powód, żeby silnik utrzymywać; znaczy natomiast, że tempo rozwoju odpowiada potrzebom jego produktów, a nie naszym.
Co ta biblioteka potrafi
Zestaw funkcji jest poważny i wart wyliczenia, bo tłumaczy, dlaczego warto się nią w ogóle zainteresować:
- Nieskończone płótno z obsługą wielu stron — to drugie jest rzadkie i przydatne, bo dokument z kilkoma tablicami jest naturalnym modelem dla projektu, nie dla pojedynczego rysunku,
- Bezgłowe komponenty dla Reacta — logika bez narzuconego wyglądu, więc interfejs dopasowuje się do systemu projektowego klienta, nie odwrotnie,
- Styl odręczny — rysunek wyglądający jak szkic, co przy makietach i schematach roboczych ma realne znaczenie komunikacyjne: odbiorca widzi, że to propozycja, nie projekt końcowy,
- Współpraca w czasie rzeczywistym przez osobną wtyczkę integrującą Yjs, czyli bibliotekę bezkonfliktowych typów replikowanych,
- Tryb ciemny z kolorami adaptacyjnymi — nie przełącznik na motyw, a przeliczanie kolorów rysunku,
- Eksport do PNG, JPEG, WebP i SVG oraz do PDF w osobnych pakietach,
- Tekst formatowany oraz eksport i import JSON — czyli dokument da się przechowywać w bazie klienta i wersjonować bez binarnego formatu.
Inteligentne kształty, czyli Lisp we frontendzie
Najbardziej nieoczywista decyzja projektowa i rzecz, która wyróżnia tę bibliotekę spośród wszystkich podobnych. Kształty nie są klasami TypeScriptu, które się rozszerza — są opisywane skryptem w osadzonym dialekcie Lispa o nazwie Mal, z wyrażeniami nawiasowymi definiującymi sposób rysowania.
W praktyce jeden skrypt określa trzy rzeczy: jak kształt jest rysowany na płótnie, jaki ma obrys i gdzie ma punkty połączeń dla linii. Wygląda to tak:
(do
(def! m 5)
(def! l (. shape :left))
(def! t (. shape :top))
(def! r (. shape :right))
(def! b (. shape :bottom))
(call canvas :polygon [
[l t]
[r t]
[r (- b m)]
[(- r m) b]
[l b]
[l t]
]))To jest rysowanie kartki z zagiętym rogiem — pięć punktów, z których jeden jest przesunięty o marginesy. Ten sam zestaw punktów zwrócony jako wartość służy potem jako obrys i jako lista punktów połączeń, więc linia doczepiona do kształtu wie, gdzie się przyklejać. Do tego dochodzi odczyt właściwości rozszerzonych kształtu, czyli parametryzacja bez pisania kodu.
Ocena tego rozwiązania jest dwustronna i warto ją mieć przed decyzją. Na plus: definicje kształtów stają się danymi, a nie kodem — da się je trzymać w bazie, edytować bez przebudowy aplikacji i dostarczać jako bibliotekę elementów dla konkretnego klienta. Przy narzędziu, w którym klient ma własny zestaw symboli branżowych, jest to bardzo mocna właściwość. Na minus: własny kształt wymaga nauczenia się języka, którym poza tym projektem nie posługuje się nikt. Nie ma tu ekosystemu, przykładów z internetu ani osoby w zespole, która to już robiła.
Architektura i trasowanie połączeń
Wewnętrzna dokumentacja projektu opisuje przepływ sterowania jednym łańcuchem: edytor przyjmuje akcje, kontroler zamienia je w zdarzenia, przekształcenia tworzą transakcje, a te mutują magazyn obiektów. Reguła jest przy tym twarda i zapisana wprost: wszystkie zmiany idą przez akcje, głównie z powodu rozwiązywania ograniczeń między kształtami. Stan nie jest utrzymywany przez rozbudowany metamodel, a serializowany prostym eksportem i importem JSON — i to była świadoma decyzja, żeby uprościć poprzednią wersję biblioteki.
Osobno opisane jest trasowanie połączeń, czyli najtrudniejsza część każdego edytora diagramów. Są dwa tryby — skośny i prostokątny — a procedura dla każdego z nich jest wypisana krok po kroku: przesunięcie punktów końcowych do środków albo do punktów połączeń, przesunięcie ich na styczne obrysu, usunięcie segmentów leżących wewnątrz kształtu i na końcu redukcja ścieżki przez usuwanie punktów według kąta. Kto próbował samodzielnie zrobić linię, która ładnie omija dwa prostokąty, doceni, że ktoś to przemyślał i zapisał.
Model kształtów
Hierarchia typów jest krótka i warto ją znać, bo to ona decyduje, czy model danych biblioteki pasuje do dziedziny klienta. U podstawy stoi Shape z pozycją i rozmiarem, mogący zawierać dzieci — więc grupowanie jest wbudowane w model, nie dorobione. Z niego wychodzą trzy gałęzie:
Box— dokłada wypełnienie, narożniki i tekst; z niego dziedzicząText,RectangleiEllipse. Wynika z tego rzecz przydatna: każdy prostokąt i każda elipsa ma tekst z definicji, więc etykiety w schemacie nie są osobnymi obiektami do pozycjonowania,Line— ma ścieżkę i typ linii, a jej pochodną jestConnectorz polami początku i końca. To rozróżnienie jest istotne: linia swobodna i połączenie między kształtami to dwa różne typy, a nie jedna rzecz z flagą,Diagram— korzeń dokumentu.
Warstwa magazynu jest równie oszczędna: fabryka tworzy kształty, przekształcenie tworzy transakcje, magazyn je stosuje, a stan edytora trzyma wszystkie trzy. Transakcja jest zbiorem mutacji — i to jest miejsce, w którym cofanie zmian oraz integracja z bezkonfliktowymi typami replikowanymi stają się naturalne, bo obie potrzebują właśnie strumienia jawnych mutacji.
Uwaga o samej dokumentacji, którą trzeba dodać dla uczciwości: ten opis architektury jest wewnętrznym brudnopisem. Plik nosi nazwę roboczą poprzedniej wersji, zawiera listę zadań do zrobienia, a część notatek jest po koreańsku. To spójne z zasadą braku wkładu z zewnątrz — dokumentacja techniczna nie jest pisana dla nas. Publiczna dokumentacja i przykład na StackBlitz istnieją osobno.
Co na tym faktycznie stoi
Przy bibliotece z pięciuset pobraniami miesięcznie pytanie „czy to w ogóle działa w produkcji" jest zasadne, a odpowiedź daje lista wdrożeń w README. Są tam cztery pozycje i warto je rozdzielić:
- Frame0 — narzędzie do szkicowych makiet o niskiej wierności, oraz DGM App — pełna aplikacja do notatek graficznych. Oba są produktami autora biblioteki, co znaczy, że silnik jest utrzymywany pod realne obciążenie komercyjne, ale też że kierunek rozwoju wyznaczają ich potrzeby,
- Nakso — tablica działająca lokalnie, i draw2app — narzędzie generujące aplikację internetową ze szkicu o niskiej wierności przy pomocy modelu językowego. Te dwa są wdrożeniami zewnętrznymi, więc pokazują, że biblioteka daje się użyć komuś spoza projektu.
To jest sytuacja typowa dla narzędzi rozwijanych w modelu „silnik pod własny produkt": jakość kodu bywa wyższa, niż sugerują statystyki pobrań, bo autor sam jest jego najbardziej wymagającym użytkownikiem. Jednocześnie brak społeczności znaczy dokładnie to, co znaczy — przy problemie zostajesz z kodem i z jedną osobą, która go zna.
Licencja: rzecz, która rozstrzyga
dgm.js jest dostępny na dwóch licencjach i strona projektu opisuje to jednoznacznie: GPL-3.0 dla oprogramowania otwartego, którego autorzy potrafią spełnić jej warunki, oraz licencja komercyjna dla oprogramowania własnościowego, gdzie kodu udostępniać się nie chce albo nie można. Cennika nie ma — po licencję komercyjną trzeba napisać na wskazany adres.
I teraz sedno, bo tu wyrobiona na serwerach intuicja prowadzi na minę. Przy oprogramowaniu serwerowym na GPL rozumowanie jest zwykle takie: uruchamiamy je u siebie, nikomu nie przekazujemy kopii, więc nie ma rozpowszechniania i nie ma obowiązków. W przypadku biblioteki frontendowej to rozumowanie nie działa, bo mechanika jest inna: pakiet JavaScriptu jest wysyłany do przeglądarki każdego użytkownika. Każdy odwiedzający otrzymuje kopię programu — a to jest, w powszechnie przyjmowanym odczytaniu tej licencji, właśnie przekazanie utworu.
Konsekwencja praktyczna dla pracy agencyjnej: zamknięta aplikacja klienta, do której pakietu wchodzi dgm.js, jest utworem opartym na programie na GPL-3.0 i wysyłanym użytkownikom. Wyjścia są dwa i oba trzeba klientowi nazwać: albo cała ta aplikacja frontendowa jest udostępniona na GPL-3.0 wraz z kodem źródłowym, albo kupuje się licencję komercyjną.
Zaznaczam wyraźnie, że to jest odczytanie licencji, nie porada prawna — a przy projekcie, w którym stawką jest kod klienta, właśnie taką sprawę oddaje się do sprawdzenia prawnikowi, zanim biblioteka wejdzie do pliku zależności. Zwracam natomiast uwagę na drugą stronę tego samego faktu: ta licencja jest oczywistym wyborem, jeśli sami budujemy coś otwartego. Wtedy GPL-3.0 nic nie kosztuje i nic nie utrudnia.
Alternatywy, zweryfikowane pod kątem licencji
Skoro licencja jest tu głównym kryterium, wypada podać, jak wygląda w narzędziach z tej samej półki. Wszystkie dane sprawdzone bezpośrednio w plikach licencji i w API GitHuba w dniu pisania:
- Excalidraw — MIT, 131 508 gwiazdek, push w dniu pisania tego tekstu. Licencyjnie bez zastrzeżeń, styl odręczny w standardzie, ekosystem nieporównywalnie większy,
- Konva — MIT (nota obejmuje też oryginalną pracę nad KineticJS), 14 776 gwiazdek, push dzień przed. Niżej poziomem: to biblioteka do rysowania na płótnie, nie gotowy edytor diagramów, więc dużo trzeba dołożyć samemu,
- tldraw — 50 226 gwiazdek, push w dniu pisania, ale licencja własnościowa firmy tldraw, nie otwarta. Sam tekst licencji definiuje osobno środowisko produkcyjne i deweloperskie i odsyła po licencje alternatywne do sprzedaży. Trzeba ją przeczytać w całości przed użyciem — jest to najbliższy odpowiednik funkcjonalny dgm.js, ale nie jest to oprogramowanie otwarte.
Wniosek dla typowego zlecenia agencyjnego jest więc dość jednoznaczny: przy zamkniętej aplikacji klienta pierwszym kandydatem jest Excalidraw, a dgm.js i tldraw wymagają albo rozmowy o licencji komercyjnej, albo otwarcia kodu.
Pułapki
- GPL-3.0 w pakiecie wysyłanym do przeglądarki to sytuacja inna niż GPL na serwerze. Najważniejsza rzecz w tym wpisie,
- Licencja komercyjna bez cennika — koszt poznasz dopiero po kontakcie, więc przy wycenie projektu jest to niewiadoma,
- Projekt nie przyjmuje pull requestów. Poprawka, której potrzebujesz, albo pojawi się, bo potrzebuje jej autor, albo nie pojawi się wcale,
- Ponad dziewięć miesięcy bez commita i 521 pobrań miesięcznie. Nie ma tu społeczności, na której można się oprzeć w razie problemu,
- Własne kształty wymagają nauki osadzonego Lispa, którego nie zna nikt poza tym projektem,
- Wewnętrzna dokumentacja techniczna nie jest pisana dla osób z zewnątrz — brudnopis z zadaniami i notatkami po koreańsku,
- Współpraca w czasie rzeczywistym to osobna wtyczka, a z nią osobny serwer synchronizacji i osobny zakres pracy,
- Eksport do PDF i do obrazów są osobnymi pakietami — warto o nich wiedzieć przy szacowaniu rozmiaru pakietu wynikowego.
Gdzie to ma sens
Zakres zastosowań jest węższy niż przy poprzednich narzędziach z tej serii, ale realny:
- Nasze własne projekty otwarte. Jeśli budujemy coś na licencji zgodnej z GPL-3.0, ta biblioteka jest do wzięcia bez żadnych kosztów i zastrzeżeń,
- Prototypy i badanie możliwości. Przykład na StackBlitz pozwala sprawdzić, czy w ogóle warto rozmawiać o takim narzędziu z klientem, bez instalowania czegokolwiek,
- Produkt z własną biblioteką kształtów branżowych. To jest przypadek, w którym skryptowane kształty naprawdę wygrywają: zestaw symboli specyficznych dla dziedziny klienta, dostarczany jako dane i zmienialny bez wdrożenia. Warto wtedy przeliczyć koszt licencji komercyjnej wobec czasu, jaki zajęłoby to samo na Excalidrawie albo Konvie,
- Wielostronicowe dokumenty ze schematami — funkcja, której w prostszych narzędziach zwykle nie ma i którą trzeba by dopisywać samemu.
Podsumowanie
- Solidny zestaw funkcji: nieskończone płótno, wiele stron, bezgłowe komponenty dla Reacta, styl odręczny, tryb ciemny z adaptacją kolorów, eksport do obrazów i PDF, JSON w obie strony,
- Kształty skryptowane w osadzonym Lispie — definicje kształtów jako dane, ale z językiem do nauczenia od zera,
- Wszystkie zmiany przez akcje, stan serializowany prostym JSON-em, trasowanie połączeń w dwóch trybach opisane krok po kroku,
- GPL-3.0 albo licencja komercyjna — a pakiet w przeglądarce znaczy, że pierwsza opcja obejmuje aplikację klienta,
- Sprawdź licencję przed funkcjami; przy zamkniętym kodzie klienta to jest pierwsza, nie ostatnia rozmowa,
- Brak przyjmowania wkładu z zewnątrz i dziewięć miesięcy ciszy przy 521 pobraniach miesięcznie,
- Do zamkniętych projektów zacznij od Excalidrawa (MIT, aktywny, ogromna społeczność); Konva daje niższy poziom na MIT, a tldraw ma licencję własnościową,
- Do projektów otwartych dgm.js jest realną i ciekawą opcją — i wtedy warto go rozważyć poważnie.
Licencja: dgm.js jest udostępniony na GPL-3.0, z osobną licencją komercyjną dostępną po kontakcie z autorem — klasyczne podwójne licencjonowanie, w którym copyleft jest wariantem darmowym, a licencja odpłatna ścieżką dla oprogramowania własnościowego. GPL-3.0 to silny copyleft: utwór oparty na programie i przekazywany dalej musi być udostępniony na tej samej licencji, wraz z kodem odpowiadającym. Kluczowa różnica wobec typowego oprogramowania serwerowego — i powód, dla którego omówiłem licencję na początku, a nie na końcu — polega na tym, że biblioteka frontendowa jest przekazywana każdemu użytkownikowi w postaci pakietu wysyłanego do jego przeglądarki. Nie ma tu sytuacji „używamy wewnętrznie, więc nie rozpowszechniamy": w powszechnie przyjmowanym odczytaniu tej licencji wysłanie skryptu do przeglądarki jest przekazaniem kopii programu. Dla zamkniętej aplikacji klienta zostają więc dwa wyjścia — udostępnienie tej aplikacji na GPL-3.0 albo zakup licencji komercyjnej — i obie ścieżki należy wycenić, zanim biblioteka trafi do pliku zależności. Nie jest to porada prawna, a wskazanie, którą sprawę trzeba oddać prawnikowi. Zaznaczę na koniec rzecz, o której przy krytyce copyleftu łatwo zapomnieć: ten model jest w pełni uczciwy — autor jasno opisuje oba warianty, nie ukrywa ograniczeń wersji darmowej i nie stosuje licencji udającej otwartość. Wybór jest po naszej stronie, a informacja potrzebna do jego dokonania jest podana wprost.