ArtCraft's thirteen open-source, clean-room creative and office apps (PhotoCraft, VectorCraft, FilmCraft, LightCraft, PdfCraft, EffectCraft, DesignCraft, WordCraft, GridCraft, DeckCraft, FigCraft, SoundCraft, CADCraft) as WebAssembly web apps. Pick the ones you want.
| Port | 8080 |
| Registry | ghcr.io/daemonless/crafting-apps |
| Source | https://github.com/storytold |
| Website | https://getartcraft.com/apps |
| Service | Image | Role | |
|---|---|---|---|
| photocraft | ghcr.io/daemonless/photocraft:latest |
Image editor (Photoshop) | docs |
| vectorcraft | ghcr.io/daemonless/vectorcraft:latest |
Vector illustration (Illustrator) | docs |
| filmcraft | ghcr.io/daemonless/filmcraft:latest |
Video editor (Premiere Pro) | docs |
| lightcraft | ghcr.io/daemonless/lightcraft:latest |
Photo library and raw developer (Lightroom) | docs |
| pdfcraft | ghcr.io/daemonless/pdfcraft:latest |
PDF workbench (Acrobat) | docs |
| effectcraft | ghcr.io/daemonless/effectcraft:latest |
Motion graphics and effects (After Effects) | docs |
| designcraft | ghcr.io/daemonless/designcraft:latest |
Page layout (InDesign) | docs |
| wordcraft | ghcr.io/daemonless/wordcraft:latest |
Document editor (Word) | docs |
| gridcraft | ghcr.io/daemonless/gridcraft:latest |
Spreadsheet (Excel) | docs |
| deckcraft | ghcr.io/daemonless/deckcraft:latest |
Presentation editor (PowerPoint) | docs |
| figcraft | ghcr.io/daemonless/figcraft:latest |
Interface design (Figma) | docs |
| soundcraft | ghcr.io/daemonless/soundcraft:latest |
Audio recording and mixing (Pro Tools) | docs |
| cadcraft | ghcr.io/daemonless/cadcraft:latest |
CAD and drafting (AutoCAD) | docs |
Before deploying, ensure your host environment is ready. See the Quick Start Guide for host setup instructions.
PhotoCraft. Image editing with layers, masks, adjustment layers and PSD files (Photoshop).
VectorCraft. Vector illustration (Illustrator).
FilmCraft. Video editing (Premiere Pro).
LightCraft. Photo library and raw developer (Lightroom).
PdfCraft. Read, organize, combine, split and secure PDFs (Acrobat).
EffectCraft. Motion graphics and visual effects (After Effects).
DesignCraft. Page layout and publishing (InDesign).
WordCraft. Document editor (Word).
GridCraft. Spreadsheet (Excel).
DeckCraft. Presentation editor (PowerPoint).
FigCraft. Interface design and prototyping (Figma).
SoundCraft. Audio recording, editing and mixing (Pro Tools).
CADCraft. Computer-aided design and drafting (AutoCAD).
These files run every part. To leave one out, delete its service from compose.yaml.
1. Save as .env and fill in what is empty:
# Crafting Apps for FreeBSD (Daemonless)
# The user and group the nginx workers run as.
PUID=1000
PGID=1000
# To set a timezone, change Etc/UTC to a TZ identifier
# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List
TZ=Etc/UTC2. Save as compose.yaml:
#
# Crafting Apps for FreeBSD (Daemonless)
#
# ArtCraft's thirteen open-source creative and office apps, each a static WebAssembly site
# served by nginx. Pick the ones you want; nothing is shared between them.
#
# Usage:
# 1. Copy example.env to .env and edit
# 2. Run: podman-compose up -d
#
name: crafting-apps
services:
photocraft:
container_name: photocraft
image: ghcr.io/daemonless/photocraft:latest
environment:
PUID: ${PUID:-1000}
PGID: ${PGID:-1000}
TZ: ${TZ:-UTC}
ports:
- "8080:80"
restart: always
vectorcraft:
container_name: vectorcraft
image: ghcr.io/daemonless/vectorcraft:latest
environment:
PUID: ${PUID:-1000}
PGID: ${PGID:-1000}
TZ: ${TZ:-UTC}
ports:
- "8081:80"
restart: always
filmcraft:
container_name: filmcraft
image: ghcr.io/daemonless/filmcraft:latest
environment:
PUID: ${PUID:-1000}
PGID: ${PGID:-1000}
TZ: ${TZ:-UTC}
ports:
- "8082:80"
restart: always
lightcraft:
container_name: lightcraft
image: ghcr.io/daemonless/lightcraft:latest
environment:
PUID: ${PUID:-1000}
PGID: ${PGID:-1000}
TZ: ${TZ:-UTC}
ports:
- "8083:80"
restart: always
pdfcraft:
container_name: pdfcraft
image: ghcr.io/daemonless/pdfcraft:latest
environment:
PUID: ${PUID:-1000}
PGID: ${PGID:-1000}
TZ: ${TZ:-UTC}
ports:
- "8084:80"
restart: always
effectcraft:
container_name: effectcraft
image: ghcr.io/daemonless/effectcraft:latest
environment:
PUID: ${PUID:-1000}
PGID: ${PGID:-1000}
TZ: ${TZ:-UTC}
ports:
- "8085:80"
restart: always
designcraft:
container_name: designcraft
image: ghcr.io/daemonless/designcraft:latest
environment:
PUID: ${PUID:-1000}
PGID: ${PGID:-1000}
TZ: ${TZ:-UTC}
ports:
- "8086:80"
restart: always
wordcraft:
container_name: wordcraft
image: ghcr.io/daemonless/wordcraft:latest
environment:
PUID: ${PUID:-1000}
PGID: ${PGID:-1000}
TZ: ${TZ:-UTC}
ports:
- "8087:80"
restart: always
gridcraft:
container_name: gridcraft
image: ghcr.io/daemonless/gridcraft:latest
environment:
PUID: ${PUID:-1000}
PGID: ${PGID:-1000}
TZ: ${TZ:-UTC}
ports:
- "8088:80"
restart: always
deckcraft:
container_name: deckcraft
image: ghcr.io/daemonless/deckcraft:latest
environment:
PUID: ${PUID:-1000}
PGID: ${PGID:-1000}
TZ: ${TZ:-UTC}
ports:
- "8089:80"
restart: always
figcraft:
container_name: figcraft
image: ghcr.io/daemonless/figcraft:latest
environment:
PUID: ${PUID:-1000}
PGID: ${PGID:-1000}
TZ: ${TZ:-UTC}
ports:
- "8090:80"
restart: always
soundcraft:
container_name: soundcraft
image: ghcr.io/daemonless/soundcraft:latest
environment:
PUID: ${PUID:-1000}
PGID: ${PGID:-1000}
TZ: ${TZ:-UTC}
ports:
- "8091:80"
restart: always
cadcraft:
container_name: cadcraft
image: ghcr.io/daemonless/cadcraft:latest
environment:
PUID: ${PUID:-1000}
PGID: ${PGID:-1000}
TZ: ${TZ:-UTC}
ports:
- "8092:80"
restart: alwaysThen run podman-compose up -d.
These files run every part. To leave one out, delete its jail from appjail-director.yml.
.env:
# .env
DIRECTOR_PROJECT=crafting-apps
PUID=1000
PGID=1000
TZ=Etc/UTC
appjail-director.yml:
# appjail-director.yml
options:
- virtualnet: :<random> default
- nat:
services:
photocraft:
name: photocraft
options:
- container: boot args:--pull=newer
- expose: 8080:80 proto:tcp
- from: ghcr.io/daemonless/photocraft:latest
oci:
user: root
environment:
- PUID: !ENV '${PUID}'
- PGID: !ENV '${PGID}'
- TZ: !ENV '${TZ}'
vectorcraft:
name: vectorcraft
options:
- container: boot args:--pull=newer
- expose: 8081:80 proto:tcp
- from: ghcr.io/daemonless/vectorcraft:latest
oci:
user: root
environment:
- PUID: !ENV '${PUID}'
- PGID: !ENV '${PGID}'
- TZ: !ENV '${TZ}'
filmcraft:
name: filmcraft
options:
- container: boot args:--pull=newer
- expose: 8082:80 proto:tcp
- from: ghcr.io/daemonless/filmcraft:latest
oci:
user: root
environment:
- PUID: !ENV '${PUID}'
- PGID: !ENV '${PGID}'
- TZ: !ENV '${TZ}'
lightcraft:
name: lightcraft
options:
- container: boot args:--pull=newer
- expose: 8083:80 proto:tcp
- from: ghcr.io/daemonless/lightcraft:latest
oci:
user: root
environment:
- PUID: !ENV '${PUID}'
- PGID: !ENV '${PGID}'
- TZ: !ENV '${TZ}'
pdfcraft:
name: pdfcraft
options:
- container: boot args:--pull=newer
- expose: 8084:80 proto:tcp
- from: ghcr.io/daemonless/pdfcraft:latest
oci:
user: root
environment:
- PUID: !ENV '${PUID}'
- PGID: !ENV '${PGID}'
- TZ: !ENV '${TZ}'
effectcraft:
name: effectcraft
options:
- container: boot args:--pull=newer
- expose: 8085:80 proto:tcp
- from: ghcr.io/daemonless/effectcraft:latest
oci:
user: root
environment:
- PUID: !ENV '${PUID}'
- PGID: !ENV '${PGID}'
- TZ: !ENV '${TZ}'
designcraft:
name: designcraft
options:
- container: boot args:--pull=newer
- expose: 8086:80 proto:tcp
- from: ghcr.io/daemonless/designcraft:latest
oci:
user: root
environment:
- PUID: !ENV '${PUID}'
- PGID: !ENV '${PGID}'
- TZ: !ENV '${TZ}'
wordcraft:
name: wordcraft
options:
- container: boot args:--pull=newer
- expose: 8087:80 proto:tcp
- from: ghcr.io/daemonless/wordcraft:latest
oci:
user: root
environment:
- PUID: !ENV '${PUID}'
- PGID: !ENV '${PGID}'
- TZ: !ENV '${TZ}'
gridcraft:
name: gridcraft
options:
- container: boot args:--pull=newer
- expose: 8088:80 proto:tcp
- from: ghcr.io/daemonless/gridcraft:latest
oci:
user: root
environment:
- PUID: !ENV '${PUID}'
- PGID: !ENV '${PGID}'
- TZ: !ENV '${TZ}'
deckcraft:
name: deckcraft
options:
- container: boot args:--pull=newer
- expose: 8089:80 proto:tcp
- from: ghcr.io/daemonless/deckcraft:latest
oci:
user: root
environment:
- PUID: !ENV '${PUID}'
- PGID: !ENV '${PGID}'
- TZ: !ENV '${TZ}'
figcraft:
name: figcraft
options:
- container: boot args:--pull=newer
- expose: 8090:80 proto:tcp
- from: ghcr.io/daemonless/figcraft:latest
oci:
user: root
environment:
- PUID: !ENV '${PUID}'
- PGID: !ENV '${PGID}'
- TZ: !ENV '${TZ}'
soundcraft:
name: soundcraft
options:
- container: boot args:--pull=newer
- expose: 8091:80 proto:tcp
- from: ghcr.io/daemonless/soundcraft:latest
oci:
user: root
environment:
- PUID: !ENV '${PUID}'
- PGID: !ENV '${PGID}'
- TZ: !ENV '${TZ}'
cadcraft:
name: cadcraft
options:
- container: boot args:--pull=newer
- expose: 8092:80 proto:tcp
- from: ghcr.io/daemonless/cadcraft:latest
oci:
user: root
environment:
- PUID: !ENV '${PUID}'
- PGID: !ENV '${PGID}'
- TZ: !ENV '${TZ}'Save the files above, then run appjail-director up.
Open http://<server>:8080 from another machine (the server's own browser can't use a published port).
Everything runs in the browser. Each app is Rust compiled to WebAssembly; the containers only serve static files. Files open through the browser's file picker and save as downloads, and nothing is stored server side, so there are no volumes.
Your work lives in the browser, not on the server. Preferences, LightCraft's photo library (in the browser's origin-private file system) and EffectCraft's offline cache belong to the browser profile that opened the app, per origin. Clearing site data, switching browsers, or reaching the app under a different hostname means starting empty. Export what you want to keep; the exported files are the backup, as with any desktop app.
HTTPS. WebGPU and the clipboard need a secure context. Over plain HTTP the apps fall back to WebGL2; put them behind your reverse proxy for the full experience.
Fonts. The browser builds ship with only two fonts. PhotoCraft, WordCraft, GridCraft, DeckCraft and CADCraft also list the Japanese, Chinese and Arabic families from craft-fonts, which their images serve alongside the app; a family is downloaded the first time you pick it or open a document that uses it.
| Variable | Default | Description |
|---|---|---|
PUID |
1000 |
User ID for the nginx worker processes |
PGID |
1000 |
Group ID for the nginx worker processes |
TZ |
Etc/UTC |
Timezone for the containers |
| Port | Protocol | Description |
|---|---|---|
80 |
TCP |
Architectures: amd64, aarch64
User: bsd (UID/GID via PUID/PGID, defaults to 1000:1000)
Base: FreeBSD 15.1
Need help? Join our Discord community.