Demo : https://configurator.combe.tf
GitHub repository : https://github.com/ABHC/keyboard-configurator
GitLab repository : https://gitlab.com/ABHC_/keyboard-configurator
- Hardware : CERN-OHL-S-2.0
- Software / Logiciel : GPL-3.0-or-later
- Documentation : CC-BY-SA-4.0
See the LICENSE file for details.
Mesa Key Configurator est une application web de personnalisation de clavier mécanique développée par Cairn Devices. Elle permet aux utilisateurs de configurer visuellement leur clavier : choix du layout, du thème graphique, des polices, et remappage avancé des touches.
Important : Ce configurateur est spécifiquement conçu pour les claviers Cairn Mesa | Key et ne prend en charge que deux architectures :
- ISO : disposition européenne standard
- Orthogonale (type TypeMatrix) : disposition avec les touches alignées verticalement
Note : Ce projet est archivé. Il est maintenu en ligne comme démo technique et peut servir d'inspiration pour des problématiques similaires de personnalisation de produit en temps réel.
Le cœur de l'application repose sur un principe simple mais puissant : un fichier SVG de base est modifié dynamiquement selon les options choisies par l'utilisateur.
Plutôt que de pré-générer des dizaines de combinaisons d'images, l'application charge un SVG template (ISO-Base.svg ou ORTHO-Base.svg) qui contient tous les éléments du clavier. Chaque personnalisation de l'utilisateur modifie directement le DOM de ce SVG en mémoire :
// Le fond est un élément <image> dans le SVG avec l'id "backgroundImage"
const imageElement = dom_keyboard.getElementById("backgroundImage");
imageElement.setAttribute("xlink:href", base64_image);// Un filtre SVG contrôle l'opacité du fond
const element = dom_keyboard.getElementById("filter");
element.style.opacity = level; // 0.1 à 0.8// La police est embarquée via une balise <style> dans le SVG
const font_face = `@font-face { font-family: '${font_name}'; src: url('${font_url}') format('truetype'); }`;
const style_tag = `<style id="embedded-font">${font_face}</style>`;
svgElement.innerHTML = `${style_tag}${svgElement.innerHTML}`;
// Puis remplacement global du nom de police dans le SVG
svgElement.innerHTML = svgElement.innerHTML.replace(new RegExp(old_font, 'gi'), new_font);// Chaque touche a des éléments "shadow" pour le contour du texte
const shadowElements = document.querySelectorAll('[id*="-shadow"]');
shadowElements.forEach(element => {
element.style.strokeWidth = stroke + 'px';
});Cette approche permet une prévisualisation instantanée sans aller-retour serveur, et le SVG final peut être exporté tel quel pour la production.
Le mode expert permet aux utilisateurs de réorganiser complètement leur clavier par glisser-déposer.
Le mode expert superpose deux couches :
- Le squelette SVG (
expert_preview_ISO.svg) : une version simplifiée du clavier affichant uniquement les contours des touches - Les touches interactives : des éléments
<div>positionnés en absolu au-dessus du SVG, chacun représentant une touche draggable
<object class="skeletton" data="architectures/expert_preview_ISO.svg" type="image/svg+xml" />
{#each Object.entries($keymap) as [key_id, key]}
<div
class="key"
style="left: {key.x}px; top: {key.y}px; width: {key.width}px; height: {key.height}px;"
draggable={isDraggable(key)}
on:dragstart={() => handleDragStart(key_id)}
on:drop={(event) => handleDrop(event, key_id)}
>
<!-- Contenu de la touche -->
</div>
{/each}Chaque touche peut avoir deux fonctions :
- Layer 1 : la fonction principale (ex:
A,1,@) - Layer 2 : la fonction secondaire accessible via FN (ex:
F1,Play,Vol+)
L'utilisateur peut basculer entre l'édition du Layer 1 et du Layer 2, ou afficher les deux simultanément :
// Structure d'une touche
{
field1: "A", // Caractère principal
field2: "a", // Caractère secondaire (minuscule)
field3: "", // Caractère tertiaire
keycode_field1: "KC_A", // Keycode QMK
layer2: "F1", // Fonction Layer 2
keycode_layer2: "KC_F1",
fixed: false, // Touche fixe (non déplaçable)
hasLayerLed: true, // Indicateur LED pour le layer 2
opacity: true // Visibilité de la touche
}Certaines touches sont protégées :
function isDraggable(key) {
if (key.fixed === true) return false; // Touches fixes (Espace, etc.)
if ($layer2_editor && !key.layer2) return false; // Pas de layer2 sur touches aveugles
if (key.opacity === false) return false; // Emplacements vides
return true;
}Les touches retirées du clavier sont stockées dans une "réserve" (KeyStash). L'utilisateur peut les glisser depuis cette réserve vers un emplacement vide du clavier.
Le centre d'aide de l'application explique comment activer les différents caractères selon leur position sur la touche :
| Position sur la touche | Comment l'activer |
|---|---|
| Caractère principal (field1) | Appui direct sur la touche |
| Caractère secondaire (field2) | Shift + touche |
| Caractère tertiaire (field3) | AltGr + touche |
| Fonction Layer 2 | FN + touche |
L'application propose une vue 3D interactive du clavier assemblé, permettant à l'utilisateur de visualiser le rendu final de sa configuration.
La prévisualisation utilise Three.js pour le rendu WebGL. Le clavier est composé de plusieurs modèles STL assemblés :
const MODELS_CONFIG = [
{ file: '/models/Keycaps-ISO.stl', texture: keycaps_texture }, // Touches
{ file: '/models/Tole-superieure-ISO.stl', texture: 'texture_alu.jpg' }, // Plaque alu
{ file: '/models/Casing.stl', color: casing_color }, // Boîtier
{ file: '/models/Amortisseur-ISO.stl', color: '#000000' }, // Amortisseur
{ file: '/models/Plaque-magnetique.stl', texture: '...' }, // Plaque magnétique
{ file: '/models/Patins.stl', color: '#444444' }, // Patins
{ file: '/models/Galets.stl', color: '#444444' }, // Galets (optionnel)
];Le SVG personnalisé par l'utilisateur est converti en texture et appliqué sur le modèle 3D des keycaps :
// La texture des touches est générée à partir du SVG configuré
{ file: '/models/Keycaps-ISO.stl', texture: keycaps_texture }L'utilisateur peut manipuler la vue 3D grâce aux OrbitControls de Three.js :
- Rotation : clic gauche + glisser
- Zoom : molette de la souris
- Pan : clic droit + glisser
Le composant gère proprement le cycle de vie des ressources WebGL :
- Chargement asynchrone des modèles STL avec
AbortControllerpour annulation - Nettoyage complet à la destruction (geometries, materials, textures, renderer)
- Génération automatique des coordonnées UV pour le mapping de texture
Frontend :
- SvelteKit 2 + Svelte 4
- TypeScript
- Three.js (visualisation 3D)
- Vite
Assets :
- Fichiers SVG pour les layouts
- Fichiers STL pour les modèles 3D
- Fichiers JSON pour les configurations (layouts, thèmes, polices, etc.)
cd frontend
npm install
npm run devL'application sera accessible sur http://localhost:5173
frontend/
├── src/routes/
│ ├── general/ # Mode standard
│ │ ├── +page.svelte # Page principale
│ │ ├── DesignPanel.svelte # Thèmes, opacité, couleurs
│ │ ├── BasePanel.svelte # Polices, switches
│ │ └── LayoutPanel.svelte # Choix du layout
│ ├── expert/ # Mode expert
│ │ ├── +page.svelte # Éditeur de keymap
│ │ ├── SkelettonKeyboard.svelte # Clavier interactif
│ │ └── KeyStash.svelte # Réserve de touches
│ ├── store.js # État global (Svelte stores)
│ └── svgEditor.js # Utilitaires de modification SVG
├── static/
│ ├── architectures/ # SVG de base (ISO, ORTHO)
│ ├── layouts_config/ # Configurations de layouts
│ ├── json/ # Données (thèmes, polices, etc.)
│ └── models/ # Fichiers STL pour la 3D
Mesa Key Configurator is a web application for customizing mechanical keyboards, developed by Cairn Devices. It allows users to visually configure their keyboard: layout selection, graphic themes, fonts, and advanced key remapping.
Important: This configurator is specifically designed for Cairn Mesa | Key keyboards and only supports two architectures:
- ISO: standard European layout
- Orthogonal (TypeMatrix-style): layout with vertically aligned keys
Note: This project is archived. It is kept online as a technical demo and may serve as inspiration for similar real-time product customization challenges.
The heart of the application relies on a simple but powerful principle: a base SVG file is dynamically modified according to user-selected options.
Rather than pre-generating dozens of image combinations, the application loads an SVG template (ISO-Base.svg or ORTHO-Base.svg) containing all keyboard elements. Each user customization directly modifies this SVG's DOM in memory:
// The background is an <image> element in the SVG with id "backgroundImage"
const imageElement = dom_keyboard.getElementById("backgroundImage");
imageElement.setAttribute("xlink:href", base64_image);// An SVG filter controls the background opacity
const element = dom_keyboard.getElementById("filter");
element.style.opacity = level; // 0.1 to 0.8// The font is embedded via a <style> tag in the SVG
const font_face = `@font-face { font-family: '${font_name}'; src: url('${font_url}') format('truetype'); }`;
const style_tag = `<style id="embedded-font">${font_face}</style>`;
svgElement.innerHTML = `${style_tag}${svgElement.innerHTML}`;
// Then global replacement of the font name in the SVG
svgElement.innerHTML = svgElement.innerHTML.replace(new RegExp(old_font, 'gi'), new_font);// Each key has "shadow" elements for text outline
const shadowElements = document.querySelectorAll('[id*="-shadow"]');
shadowElements.forEach(element => {
element.style.strokeWidth = stroke + 'px';
});This approach enables instant preview without server round-trips, and the final SVG can be exported as-is for production.
Expert mode allows users to completely reorganize their keyboard through drag-and-drop.
Expert mode overlays two layers:
- The SVG skeleton (
expert_preview_ISO.svg): a simplified keyboard version showing only key outlines - Interactive keys:
<div>elements absolutely positioned above the SVG, each representing a draggable key
<object class="skeletton" data="architectures/expert_preview_ISO.svg" type="image/svg+xml" />
{#each Object.entries($keymap) as [key_id, key]}
<div
class="key"
style="left: {key.x}px; top: {key.y}px; width: {key.width}px; height: {key.height}px;"
draggable={isDraggable(key)}
on:dragstart={() => handleDragStart(key_id)}
on:drop={(event) => handleDrop(event, key_id)}
>
<!-- Key content -->
</div>
{/each}Each key can have two functions:
- Layer 1: the primary function (e.g.,
A,1,@) - Layer 2: the secondary function accessible via FN (e.g.,
F1,Play,Vol+)
Users can switch between editing Layer 1 and Layer 2, or display both simultaneously:
// Key structure
{
field1: "A", // Main character
field2: "a", // Secondary character (lowercase)
field3: "", // Tertiary character
keycode_field1: "KC_A", // QMK keycode
layer2: "F1", // Layer 2 function
keycode_layer2: "KC_F1",
fixed: false, // Fixed key (non-movable)
hasLayerLed: true, // LED indicator for layer 2
opacity: true // Key visibility
}Certain keys are protected:
function isDraggable(key) {
if (key.fixed === true) return false; // Fixed keys (Space, etc.)
if ($layer2_editor && !key.layer2) return false; // No layer2 on blind keys
if (key.opacity === false) return false; // Empty slots
return true;
}Keys removed from the keyboard are stored in a "stash" (KeyStash). Users can drag them from this stash to an empty keyboard slot.
The application's help center explains how to activate different characters based on their position on the key:
| Position on key | How to activate |
|---|---|
| Main character (field1) | Direct key press |
| Secondary character (field2) | Shift + key |
| Tertiary character (field3) | AltGr + key |
| Layer 2 function | FN + key |
The application offers an interactive 3D view of the assembled keyboard, allowing users to visualize the final rendering of their configuration.
The preview uses Three.js for WebGL rendering. The keyboard is composed of several assembled STL models:
const MODELS_CONFIG = [
{ file: '/models/Keycaps-ISO.stl', texture: keycaps_texture }, // Keys
{ file: '/models/Tole-superieure-ISO.stl', texture: 'texture_alu.jpg' }, // Alu plate
{ file: '/models/Casing.stl', color: casing_color }, // Case
{ file: '/models/Amortisseur-ISO.stl', color: '#000000' }, // Dampener
{ file: '/models/Plaque-magnetique.stl', texture: '...' }, // Magnetic plate
{ file: '/models/Patins.stl', color: '#444444' }, // Feet
{ file: '/models/Galets.stl', color: '#444444' }, // Pebbles (optional)
];The user-customized SVG is converted to a texture and applied to the 3D keycaps model:
// The keycap texture is generated from the configured SVG
{ file: '/models/Keycaps-ISO.stl', texture: keycaps_texture }Users can manipulate the 3D view using Three.js OrbitControls:
- Rotate: left click + drag
- Zoom: mouse wheel
- Pan: right click + drag
The component properly manages WebGL resource lifecycle:
- Asynchronous STL model loading with
AbortControllerfor cancellation - Complete cleanup on destruction (geometries, materials, textures, renderer)
- Automatic UV coordinate generation for texture mapping
Frontend:
- SvelteKit 2 + Svelte 4
- TypeScript
- Three.js (3D visualization)
- Vite
Assets:
- SVG files for layouts
- STL files for 3D models
- JSON files for configurations (layouts, themes, fonts, etc.)
cd frontend
npm install
npm run devThe application will be available at http://localhost:5173
frontend/
├── src/routes/
│ ├── general/ # Standard mode
│ │ ├── +page.svelte # Main page
│ │ ├── DesignPanel.svelte # Themes, opacity, colors
│ │ ├── BasePanel.svelte # Fonts, switches
│ │ └── LayoutPanel.svelte # Layout selection
│ ├── expert/ # Expert mode
│ │ ├── +page.svelte # Keymap editor
│ │ ├── SkelettonKeyboard.svelte # Interactive keyboard
│ │ └── KeyStash.svelte # Key stash
│ ├── store.js # Global state (Svelte stores)
│ └── svgEditor.js # SVG modification utilities
├── static/
│ ├── architectures/ # Base SVGs (ISO, ORTHO)
│ ├── layouts_config/ # Layout configurations
│ ├── json/ # Data (themes, fonts, etc.)
│ └── models/ # STL files for 3D