Open Pencil vs Penpot : Comparaison d'architecture et de performances
Pourquoi comparer ? OpenPencil existe parce que les plateformes de design fermées contrôlent ce qui est possible. Comprendre les différences architecturales montre ce qu'une alternative ouverte et local-first peut faire différemment.
Renderer WASM de Penpot
Penpot 2.x inclut un renderer Rust/Skia WASM (render-wasm/v1) activable via les flags du serveur ou le paramètre URL ?wasm=true. Le renderer SVG reste le défaut. Cette page couvre les deux.
1. Échelle et taille du code
| Métrique | Open Pencil | Penpot |
|---|---|---|
| LOC total | ~26 000 | ~299 000 |
| Fichiers source | ~143 | ~2 900 |
| Langages | TypeScript, Vue | Clojure, ClojureScript, Rust, JS, SQL, SCSS |
| Moteur de rendu | ~3 200 LOC (TS, 10 plików) | 22 000 LOC (Rust/Skia WASM) |
| Code UI | ~4 500 LOC | ~175 000 LOC (CLJS + SCSS) |
| Backend | Aucun (local-first) | 32 600 LOC + 151 fichiers SQL |
| Ratio LOC | 1× | ~11× |
Open Pencil est ~11× plus petit — et c'est tout l'intérêt. Ce n'est pas une simplification ; c'est une architecture fondamentalement différente.
2. Architecture
Open Pencil : Client monolithique
┌─────────────────────────────────┐
│ Tauri (shell natif) │
│ ┌───────────────────────────┐ │
│ │ Vue 3 + TypeScript │ │
│ │ ┌─────────┐ ┌──────────┐│ │
│ │ │ Editor │ │ Kiwi ││ │
│ │ │ Store │ │ Codec ││ │
│ │ └────┬─────┘ └──────────┘│ │
│ │ │ │ │
│ │ ┌────▼────────────────┐ │ │
│ │ │ Scene Graph (TS) │ │ │
│ │ │ Map<string, Node> │ │ │
│ │ └────┬────────────────┘ │ │
│ │ │ │ │
│ │ ┌────▼────┐ ┌──────────┐│ │
│ │ │ Skia │ │ Yoga ││ │
│ │ │CanvasKit│ │ Layout ││ │
│ │ │ (WASM) │ │ (WASM) ││ │
│ │ └─────────┘ └──────────┘│ │
│ └───────────────────────────┘ │
└─────────────────────────────────┘Tout dans un seul processus. Pas de serveur, pas de base de données, pas de Docker.
Penpot : Client-serveur distribué
┌───────────────────────────────────────────────────────┐
│ Docker Compose │
│ ┌──────────────┐ ┌─────────────┐ ┌──────────────┐ │
│ │ Frontend │ │ Backend │ │ Exporter │ │
│ │ ClojureScript│ │ Clojure │ │ (Chromium) │ │
│ │ shadow-cljs │ │ JVM │ │ │ │
│ │ ┌─────────┐ │ │ ┌────────┐ │ └──────────────┘ │
│ │ │render- │ │ │ │Postgres│ │ │
│ │ │wasm │ │ │ │Valkey │ │ ┌──────────────┐ │
│ │ │(Rust→ │ │ │ │ MinIO │ │ │ MCP │ │
│ │ │ Skia │ │ │ └────────┘ │ │ Server │ │
│ │ │ WASM) │ │ │ │ └──────────────┘ │
│ │ └─────────┘ │ │ │ │
│ └──────────────┘ └─────────────┘ │
└───────────────────────────────────────────────────────┘Minimum 5+ services. PostgreSQL, Redis (Valkey), MinIO, un backend JVM, un exporteur Node.js (Chromium headless), plus le frontend ClojureScript.
Verdict : Architecture
L'architecture monoprocessus d'Open Pencil élimine : la latence réseau, l'overhead de sérialisation aux frontières de services, la complexité d'orchestration de conteneurs et l'overhead de requêtes base de données.
3. Pipeline de rendu
Open Pencil : TS → CanvasKit WASM (direct)
renderSceneToCanvas(canvas, graph, pageId) {
this.fillPaint.setColor(...)
canvas.drawRRect(rrect, this.fillPaint)
}- 1 franchissement de frontière : TS → WASM (CanvasKit)
- Le graphe de scène vit dans le heap JS — pas de sérialisation pour le rendu
- 1 646 LOC de renderer au total
Penpot : JS (compilé depuis CLJS) → Rust WASM → Skia
ClojureScript (compilé en JS)
→ décomposer en primitives + empaqueter en binaire en mémoire linéaire WASM
→ Rust WASM (via Emscripten C FFI)
→ skia-safe (bindings Rust Skia)
→ Skia (WebGL)Quand désactivé (défaut), les formes sont rendues comme un arbre DOM SVG. Quand activé, système de rendu par tuiles avec 11 surfaces et cache de 1024 entrées.
Verdict : Rendu
| Aspect | Open Pencil | Penpot |
|---|---|---|
| Frontière JS→WASM | Direct (objets TS) | Empaquetage binaire (104 octets par forme) |
| Modèle de rendu | Immédiat/redessin complet | Cache par tuiles |
| Gestion des surfaces | 1 surface | 11 surfaces |
| Overhead mémoire | Faible (pas de cache tuiles) | Élevé (1024 entrées cache) |
| Complexité du code | 1 646 LOC | 22 000 LOC |
| Code unsafe | Aucun | État global unsafe |
4. Graphe de scène et modèle de données
Open Pencil
nodes: Map<string, SceneNode>
// 29 types de nœuds du schéma Kiwi de Figma
// ~390 champs par NodeChange (compatible Figma)- Interfaces TypeScript avec types stricts
- GUIDs au format
sessionID:localIDde Figma
Penpot
- Données réparties dans
common/(49 600 LOC de .cljc) - Validation de schéma au runtime (Malli)
- Les données doivent traverser la frontière CLJS→Rust pour le rendu
Verdict : Modèle de données
Open Pencil réutilise le schéma éprouvé de Figma directement en TypeScript — zéro traduction. Penpot maintient son propre système de types à travers trois langages.
5. Moteur de layout
Open Pencil : Yoga WASM (314 LOC)
import Yoga from 'yoga-layout'
const root = Yoga.Node.create()
root.setFlexDirection(FlexDirection.Row)
root.calculateLayout()314 lignes au total. Synchrone, dans le processus.
Penpot : Double implémentation
Penpot maintient deux moteurs de layout indépendants (CLJS et Rust) qui doivent produire des résultats identiques. ~3 000+ LOC de code layout personnalisé dupliqué.
Verdict : Layout
Open Pencil délègue à Yoga (utilisé par React Native sur des milliards d'appareils) en 314 lignes.
6. Format de fichier et compatibilité Figma
Open Pencil
- Format binaire Kiwi natif — même sérialisation que Figma
- Import direct
.fig, collage depuis le presse-papiers Figma - Compatible avec le protocole multiplayer de Figma
Penpot
- Archive ZIP (
.penpot) avec manifestes JSON et assets binaires - Pas d'import natif
.fig - Trois versions de format avec système de migration
Verdict : Format de fichier
Open Pencil a un avantage significatif — il peut lire les fichiers Figma nativement et coller les données du presse-papiers Figma.
7. Gestion d'état et annulation
Open Pencil
// 110 LOC — patron de commande inverse
class UndoManager {
apply(entry) { entry.forward(); this.undoStack.push(entry) }
undo() { entry.inverse(); this.redoStack.push(entry) }
}110 lignes. Closures forward/inverse avec état minimal.
Penpot
Gestion d'état via Potok (bibliothèque Redux-like pour atomes ClojureScript). Undo stocke des vecteurs de changements inverses (max 50 entrées), avec auto-expiration après 20 secondes.
8. Expérience développeur
| Métrique | Open Pencil | Penpot |
|---|---|---|
| Setup dev | bun install && bun dev | Docker Compose + JVM + Node + Rust |
| Rechargement à chaud | Vite HMR (~50ms) | shadow-cljs (secondes) |
| Vérification de types | TypeScript (strict) | Runtime (schémas Malli) |
| Temps de build | <5s (Vite) | Minutes (JVM + CLJS + Rust WASM) |
| Barrière première contribution | Basse (TS/Vue) | Haute (Clojure + Rust + Docker) |
| Desktop | Tauri v2 (~5Mo) | N/A (navigateur uniquement) |
| Bassin de recrutement | Massif (devs TS/Vue) | Petit (ClojureScript + Rust) |
9. Caractéristiques de performance
| Scénario | Open Pencil | Penpot |
|---|---|---|
| Démarrage à froid | <2s (chargement WASM) | 10s+ (serveur + client + WASM) |
| Latence d'opération | <1ms (dans le processus) | 10-50ms (aller-retour réseau) |
| Frame de rendu | Appel Skia direct | CLJS→JS→WASM FFI→Skia |
| Mémoire de base | ~50Mo (onglet navigateur) | ~300Mo+ (JVM + Postgres + Valkey + navigateur) |
| Capacité hors ligne | Complète (local-first) | Aucune (dépend du serveur) |
| Rendu 10K formes | Une passe, sans cache | Par tuiles avec 11 surfaces |
10. Ce que Penpot fait mieux
- Collaboration serveur — édition multi-utilisateurs centralisée avec WebSockets, comptes et contrôle d'accès
- Export PDF serveur — service d'export Chromium headless pour PDF (Open Pencil exporte déjà en SVG nativement)
- Système de plugins — API complète avec exécution sandboxée
- Tokens de design — support natif des design tokens
- CSS Grid layout — implémentation personnalisée (Open Pencil attend Yoga Grid)
- Self-hosting — déploiement Docker pour les équipes
- Maturité — années d'utilisation en production
11. Scripting et extensibilité
OpenPencil inclut une commande eval offrant une API Plugin compatible Figma pour le scripting headless. De plus, 90 outils IA sont disponibles via le chat intégré, le serveur MCP (stdio + HTTP) et le CLI. Penpot a un système de plugins avec exécution sandboxée mais pas d'API de scripting headless ni d'intégration MCP.
Résumé
| Dimension | Gagnant | Pourquoi |
|---|---|---|
| Simplicité architecturale | Open Pencil | Un processus vs 5+ services |
| Performance de rendu | Open Pencil | CanvasKit direct vs SVG DOM (défaut) ou WASM empaqueté |
| Maintenabilité du code | Open Pencil | ~26K LOC en 1 langage vs 299K en 4+ |
| Compatibilité Figma | Open Pencil | Codec Kiwi natif vs pas de support .fig |
| Onboarding développeur | Open Pencil | TS/Vue vs Clojure/Rust/Docker |
| Expérience desktop | Open Pencil | Tauri natif vs navigateur uniquement |
| Moteur de layout | Open Pencil | Yoga (éprouvé) vs double implémentation |
| Collaboration | Égalité | Penpot : serveur avec contrôle d'accès ; Open Pencil : P2P via Trystero + Yjs |
| Self-hosting | Penpot | Prêt Docker vs desktop uniquement |
| Maturité écosystème | Penpot | Années de production vs stade précoce |
Open Pencil est architecturalement plus léger — un renderer CanvasKit monoprocessus en ~26K LOC TypeScript, compatible Figma par conception. Penpot est une plateforme full-stack avec ~299K LOC. Open Pencil a le scripting headless, 90 outils AI/MCP, export SVG et une app desktop native.