diff --git a/.gitignore b/.gitignore
index d73afad..71cbc8a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,4 @@
 .envrc
 .direnv
-node_modules
 result
 out.*
diff --git a/flake.nix b/flake.nix
index 94c4a00..3ac1563 100644
--- a/flake.nix
+++ b/flake.nix
@@ -7,12 +7,13 @@
       (system:
         let
           pkgs = nixpkgs.legacyPackages.${system};
-          swoop = pkgs.callPackage ./swoop.nix { };
+          layout-lens = pkgs.callPackage ./layout-lens/default.nix { };
         in
         rec {
-          packages.swoop = swoop;
-          defaultPackage = packages.swoop;
-          devShell = pkgs.callPackage ./shell.nix { };
+          packages.layout-lens = layout-lens;
+          defaultPackage = packages.layout-lens;
+          devShells.layout-lens = pkgs.callPackage ./layout-lens/shell.nix { };
+          devShells.qmk = pkgs.callPackage ./keyboards/qmk/shell.nix { };
         }
       );
 }
diff --git a/keyboards/qmk/ferris-sweep/lens.json b/keyboards/qmk/ferris-sweep/lens.json
new file mode 100644
index 0000000..e51c902
--- /dev/null
+++ b/keyboards/qmk/ferris-sweep/lens.json
@@ -0,0 +1,49 @@
+{
+  "colorscheme": {
+    "keyFill": "#ffffff",
+    "keyStroke": "#000000",
+    "mainLayerColor": "black",
+    "tlLayerColor": "blue",
+    "trLayerColor": "red",
+    "blLayerColor": "purple"
+  },
+  "keys": [
+    ["Q", "!", "1", "f1"],
+    ["A", "<", "6", "f6"],
+    ["X", ">", "", "f11"],
+    ["W", "@", "2", "f2"],
+    ["R", "(", "7", "f7"],
+    ["C", "]", "", "f12"],
+    ["F", "#", "3", "f3"],
+    ["S", "[", "8", "f8"],
+    ["D", "]"],
+    ["P", "$", "4", "f4"],
+    ["T", "{", "9", "f9"],
+    ["V", "}"],
+    ["B", "%", "5", "f5"],
+    ["G", "-", "0", "f10"],
+    ["Z", "—"],
+    ["TR", "", ""],
+    ["␣", "", ""],
+    ["⇧", "", ""],
+    ["TL", "", ""],
+    ["J", "^", "🏠"],
+    ["M", "?", "◄", "😱"],
+    ["K", "", "", "🎮"],
+    ["L", "&", "⏬", "🔊"],
+    ["N", "_", "▼", "🔉"],
+    ["H", "|", "", "🔇"],
+    ["U", "*", "⏫", "🔆"],
+    ["E", "/", "▲", "🔅"],
+    [",", "\\", ""],
+    ["Y", "~", "end", "⏪"],
+    ["I", "=", "►", "⏯️"],
+    [".", "+", "", "⏩"],
+    [":", "`", "del", "copy"],
+    ["O", ";", "", "paste"],
+    ["'", "\"", "", "cut"]
+  ],
+  "imagePadding": 20,
+  "keySize": 50,
+  "layout": "split_3x5_2"
+}
diff --git a/keyboards/qmk/shell.nix b/keyboards/qmk/shell.nix
new file mode 100644
index 0000000..6dc1c19
--- /dev/null
+++ b/keyboards/qmk/shell.nix
@@ -0,0 +1,5 @@
+{ pkgs ? import <nixpkgs> { } }:
+with pkgs;
+mkShell {
+  buildInputs = with pkgs; [ qmk ];
+}
diff --git a/layout-lens/.gitignore b/layout-lens/.gitignore
new file mode 100644
index 0000000..3c3629e
--- /dev/null
+++ b/layout-lens/.gitignore
@@ -0,0 +1 @@
+node_modules
diff --git a/swoop.nix b/layout-lens/default.nix
similarity index 100%
rename from swoop.nix
rename to layout-lens/default.nix
diff --git a/package-lock.json b/layout-lens/package-lock.json
similarity index 100%
rename from package-lock.json
rename to layout-lens/package-lock.json
diff --git a/package.json b/layout-lens/package.json
similarity index 100%
rename from package.json
rename to layout-lens/package.json
diff --git a/shell.nix b/layout-lens/shell.nix
similarity index 100%
rename from shell.nix
rename to layout-lens/shell.nix
diff --git a/layout-lens/src/config.ts b/layout-lens/src/config.ts
new file mode 100644
index 0000000..e1590a6
--- /dev/null
+++ b/layout-lens/src/config.ts
@@ -0,0 +1,62 @@
+import {
+  Arguments,
+  Config,
+  KeyboardKey,
+  KeySymbol,
+  Layout,
+  PredefinedLayout,
+  PredefinedLayoutName,
+  SpecialSymbols,
+} from "./types";
+import split_3x5_2 from "./layouts/split_3x5_2";
+
+export function parseConfig(input: string): Config {
+  const parsed = JSON.parse(input);
+
+  const layout = PredefinedLayoutName[
+    parsed.layout as string
+  ] as PredefinedLayoutName;
+
+  if (layout === undefined) {
+    throw `Cannot find layout ${parsed.layout}`;
+  }
+
+  return {
+    keys: (parsed.keys as string[][]).map((k) =>
+      k.map((s) => {
+        const special = SpecialSymbols[s];
+        if (special === undefined) return s;
+        return special as SpecialSymbols;
+      }),
+    ),
+    colorscheme: parsed.colorscheme,
+    imagePadding: parsed.imagePadding,
+    keySize: parsed.keySize,
+    layout,
+  };
+}
+
+function key(
+  main: KeySymbol,
+  tlLayer: KeySymbol = "",
+  trLayer: KeySymbol = "",
+  blLayer: KeySymbol = "",
+): KeyboardKey {
+  return { main, tlLayer, trLayer, blLayer };
+}
+
+const layouts: Record<PredefinedLayoutName, PredefinedLayout> = {
+  [PredefinedLayoutName.split_3x5_2]: split_3x5_2,
+};
+
+export function makeLayout(config: Config): Layout {
+  const predefined = layouts[config.layout](config.keySize);
+  return {
+    keys: config.keys.map((k) => key(...(k as Arguments<typeof key>))),
+    colorscheme: config.colorscheme,
+    imagePadding: config.imagePadding,
+    keySize: config.keySize,
+    visual: predefined.visual,
+    size: predefined.size,
+  };
+}
diff --git a/layout-lens/src/index.ts b/layout-lens/src/index.ts
new file mode 100644
index 0000000..1f265e4
--- /dev/null
+++ b/layout-lens/src/index.ts
@@ -0,0 +1,12 @@
+#!/usr/bin/env node
+import * as fs from "fs";
+import { renderLayout } from "./render";
+import { makeLayout, parseConfig } from "./config";
+
+const inPath = process.argv[2];
+const outPath = process.argv[3];
+
+const input = fs.readFileSync(inPath, "utf8");
+const layout = makeLayout(parseConfig(input));
+
+fs.writeFileSync(outPath, renderLayout(layout));
diff --git a/layout-lens/src/layout.ts b/layout-lens/src/layout.ts
new file mode 100644
index 0000000..c2f1b8e
--- /dev/null
+++ b/layout-lens/src/layout.ts
@@ -0,0 +1,56 @@
+import { VisualKey, VisualLayout } from "./types";
+import * as V from "./vec2";
+
+function visualKey(at: V.Vec2, keySize: number, angle: number = 0): VisualKey {
+  return { position: at, size: [keySize, keySize], angle };
+}
+
+function col(at: V.Vec2, keySize: number): VisualLayout {
+  return [
+    visualKey(at, keySize),
+    visualKey(V.add(at, [0, keySize]), keySize),
+    visualKey(V.add(at, [0, 2 * keySize]), keySize),
+  ];
+}
+
+function radians(deg: number): number {
+  return (deg / 180) * Math.PI;
+}
+
+export function thumbs(
+  at: V.Vec2,
+  reverse: boolean,
+  keySize: number,
+  thumbRotation = reverse ? -15 : 15,
+): VisualLayout {
+  // Distance between thumb key centers
+  const factor = keySize;
+
+  const offset: V.Vec2 = [
+    Math.cos(radians(thumbRotation)) * factor,
+    Math.sin(radians(thumbRotation)) * factor,
+  ];
+
+  const result = [
+    visualKey(at, keySize, thumbRotation),
+    visualKey(
+      V.add(at, reverse ? V.neg(offset) : offset),
+      keySize,
+      thumbRotation,
+    ),
+  ];
+
+  if (reverse) result.reverse();
+
+  return result;
+}
+
+export function cols(
+  at: V.Vec2,
+  cols: V.Vec2[],
+  keySize: number,
+): VisualLayout {
+  return cols.flatMap((self, index) =>
+    col(V.add(at, V.add(self, [index * keySize, 0])), keySize),
+  );
+}
diff --git a/layout-lens/src/layouts/split_3x5_2.ts b/layout-lens/src/layouts/split_3x5_2.ts
new file mode 100644
index 0000000..5f88871
--- /dev/null
+++ b/layout-lens/src/layouts/split_3x5_2.ts
@@ -0,0 +1,26 @@
+import * as L from "../layout";
+import type { PredefinedLayout } from "../types";
+import type { Vec2 } from "../vec2";
+
+const layout: PredefinedLayout = (keySize) => {
+  // 3x5 block
+  const block: Vec2[] = [
+    [0, keySize],
+    [0, keySize / 2],
+    [0, 0],
+    [0, keySize / 2],
+    [0, keySize],
+  ];
+
+  return {
+    visual: [
+      L.cols([0, 0], block, keySize),
+      L.thumbs([keySize * 3.5, keySize * 4.5], false, keySize),
+      L.thumbs([keySize * 7.5, keySize * 4.5], true, keySize),
+      L.cols([7 * keySize, 0], block, keySize),
+    ].flat(),
+    size: [keySize * 12, keySize * 6],
+  };
+};
+
+export default layout;
diff --git a/layout-lens/src/render.ts b/layout-lens/src/render.ts
new file mode 100644
index 0000000..6b5f730
--- /dev/null
+++ b/layout-lens/src/render.ts
@@ -0,0 +1,124 @@
+import { tag, px } from "./svg";
+import {
+  KeyboardKey,
+  KeySymbol,
+  Layout,
+  LayoutColorscheme,
+  SpecialSymbols,
+  VisualKey,
+} from "./types";
+
+function textContents(input: KeySymbol): string {
+  if (input === SpecialSymbols.TL || input === SpecialSymbols.TR) return "■";
+
+  return input;
+}
+
+function renderKey(
+  visual: VisualKey,
+  key: KeyboardKey,
+  colorscheme: LayoutColorscheme,
+  keySize: number,
+) {
+  const centerX = visual.position[0] + visual.size[0] / 2;
+  const centerY = visual.position[1] + visual.size[1] / 2;
+  const textAttribs = {
+    "text-anchor": "middle",
+    "dominant-baseline": "middle",
+    "font-family": "Helvetica",
+  };
+
+  const textColor = (input: KeySymbol, _default: string): string => {
+    if (input === SpecialSymbols.TL) return colorscheme.tlLayerColor;
+    if (input === SpecialSymbols.TR) return colorscheme.trLayerColor;
+    return _default;
+  };
+
+  return tag(
+    "g",
+    {
+      transform:
+        visual.angle && visual.angle !== 0
+          ? `rotate(${visual.angle}, ${centerX}, ${centerY})`
+          : undefined,
+    },
+    [
+      tag("rect", {
+        width: px(visual.size[0]),
+        height: px(visual.size[1]),
+        x: visual.position[0],
+        y: visual.position[1],
+        fill: colorscheme.keyFill,
+        stroke: colorscheme.keyStroke,
+        "stroke-width": px(2),
+      }),
+      tag(
+        "text",
+        {
+          x: centerX,
+          y: centerY,
+          textLength: px(keySize / 2),
+          fill: textColor(key.main, colorscheme.mainLayerColor),
+          ...textAttribs,
+        },
+        textContents(key.main),
+      ),
+      tag(
+        "text",
+        {
+          x: visual.position[0] + visual.size[0] / 6,
+          y: visual.position[1] + visual.size[1] / 6,
+          fill: textColor(key.tlLayer, colorscheme.tlLayerColor),
+          "font-size": "66%",
+          ...textAttribs,
+        },
+        textContents(key.tlLayer),
+      ),
+      tag(
+        "text",
+        {
+          x: visual.position[0] + (9 * visual.size[0]) / 10,
+          y: visual.position[1] + visual.size[1] / 6,
+          fill: textColor(key.trLayer, colorscheme.trLayerColor),
+          "font-size": "66%",
+          ...textAttribs,
+          "text-anchor": "end",
+        },
+        textContents(key.trLayer),
+      ),
+      tag(
+        "text",
+        {
+          x: visual.position[0] + visual.size[0] / 10,
+          y: visual.position[1] + (5 * visual.size[1]) / 6,
+          fill: textColor(key.blLayer, colorscheme.blLayerColor),
+          "font-size": "66%",
+          ...textAttribs,
+          "text-anchor": "start",
+        },
+        textContents(key.blLayer),
+      ),
+    ].join("\n"),
+  );
+}
+
+export function renderLayout(layout: Layout) {
+  return tag(
+    "svg",
+    {
+      viewBox: [
+        -layout.imagePadding,
+        -layout.imagePadding,
+        2 * layout.imagePadding + layout.size[0],
+        2 * layout.imagePadding + layout.size[1],
+      ].join(" "),
+      xmlns: "http://www.w3.org/2000/svg",
+      "xmlns:xlink": "http://www.w3.org/1999/xlink",
+    },
+    layout.visual
+      .map((key, index) =>
+        renderKey(key, layout.keys[index], layout.colorscheme, layout.keySize),
+      )
+      .join("\n"),
+  );
+}
diff --git a/layout-lens/src/svg.ts b/layout-lens/src/svg.ts
new file mode 100644
index 0000000..4a0eab5
--- /dev/null
+++ b/layout-lens/src/svg.ts
@@ -0,0 +1,30 @@
+function indent(amount: number, text: string) {
+  return text
+    .split("\n")
+    .map((l) => " ".repeat(amount) + l)
+    .join("\n");
+}
+
+export function tag(
+  name: string,
+  attributes: Record<string, string | number | undefined>,
+  children: string = "",
+) {
+  const attributeString = Object.entries(attributes)
+    .map(([k, v]) => `${k}="${v}"`)
+    .join(" ");
+
+  const result = [
+    `<${name}${attributeString === "" ? "" : ` ${attributeString}`}>`,
+    indent(2, children),
+    `</${name}>`,
+  ]
+    .filter((l) => l.trim() !== "")
+    .join("\n");
+
+  return result;
+}
+
+export function px(value: number) {
+  return `${value}px`;
+}
diff --git a/layout-lens/src/types.ts b/layout-lens/src/types.ts
new file mode 100644
index 0000000..ab2e524
--- /dev/null
+++ b/layout-lens/src/types.ts
@@ -0,0 +1,65 @@
+import type { Vec2 } from "./vec2";
+
+/** Returns the arguments of a given function type  */
+export type Arguments<T extends (...args: any) => any> = T extends (
+  ...args: infer U
+) => any
+  ? U
+  : never;
+
+export interface VisualKey {
+  position: Vec2;
+  size: Vec2;
+  angle?: number;
+}
+
+export type VisualLayout = VisualKey[];
+
+export enum SpecialSymbols {
+  TL,
+  TR,
+}
+
+export type KeySymbol = SpecialSymbols | string;
+
+export interface KeyboardKey {
+  main: KeySymbol;
+  tlLayer: KeySymbol;
+  trLayer: KeySymbol;
+  blLayer: KeySymbol;
+}
+
+export interface LayoutColorscheme {
+  keyFill: string;
+  keyStroke: string;
+  mainLayerColor: string;
+  tlLayerColor: string;
+  trLayerColor: string;
+  blLayerColor: string;
+}
+
+export interface Layout {
+  visual: VisualLayout;
+  keys: KeyboardKey[];
+  colorscheme: LayoutColorscheme;
+  imagePadding: number;
+  size: Vec2;
+  keySize: number;
+}
+
+export enum PredefinedLayoutName {
+  split_3x5_2,
+}
+
+export interface Config {
+  keys: KeySymbol[][];
+  colorscheme: LayoutColorscheme;
+  imagePadding: number;
+  keySize: number;
+  layout: PredefinedLayoutName;
+}
+
+export type PredefinedLayout = (keySize: number) => {
+  visual: VisualLayout;
+  size: Vec2;
+};
diff --git a/layout-lens/src/vec2.ts b/layout-lens/src/vec2.ts
new file mode 100644
index 0000000..fc99054
--- /dev/null
+++ b/layout-lens/src/vec2.ts
@@ -0,0 +1,9 @@
+export type Vec2 = [number, number];
+
+export function add(x: Vec2, y: Vec2): Vec2 {
+  return [x[0] + y[0], x[1] + y[1]];
+}
+
+export function neg(v: Vec2): Vec2 {
+  return [-v[0], -v[1]];
+}
diff --git a/src/index.ts b/src/index.ts
deleted file mode 100644
index e9339fb..0000000
--- a/src/index.ts
+++ /dev/null
@@ -1,309 +0,0 @@
-#!/usr/bin/env node
-import * as fs from "fs";
-
-type Vec2 = [number, number];
-
-interface VisualKey {
-  position: Vec2;
-  size: Vec2;
-  angle?: number;
-}
-
-type VisualLayout = VisualKey[];
-
-interface KeyboardKey {
-  main: string;
-  tlLayer: string;
-  trLayer: string;
-  blLayer: string;
-}
-
-interface LayoutColorscheme {
-  keyFill: string;
-  keyStroke: string;
-  mainLayerColor: string;
-  tlLayerColor: string;
-  trLayerColor: string;
-  blLayerColor: string;
-}
-
-interface Layout {
-  visual: VisualLayout;
-  keys: KeyboardKey[];
-  colorscheme: LayoutColorscheme;
-  padding: number;
-  size: Vec2;
-}
-
-function indent(amount: number, text: string) {
-  return text
-    .split("\n")
-    .map((l) => " ".repeat(amount) + l)
-    .join("\n");
-}
-
-function tag(
-  name: string,
-  attributes: Record<string, string | number | undefined>,
-  children: string = "",
-) {
-  const attributeString = Object.entries(attributes)
-    .map(([k, v]) => `${k}="${v}"`)
-    .join(" ");
-
-  const result = [
-    `<${name}${attributeString === "" ? "" : ` ${attributeString}`}>`,
-    indent(2, children),
-    `</${name}>`,
-  ]
-    .filter((l) => l.trim() !== "")
-    .join("\n");
-
-  return result;
-}
-
-function px(value: number) {
-  return `${value}px`;
-}
-
-function textContents(input: string): string {
-  if (input === "TR" || input === "TL") return "■";
-  return input;
-}
-
-function renderKey(
-  visual: VisualKey,
-  key: KeyboardKey,
-  colorscheme: LayoutColorscheme,
-) {
-  const centerX = visual.position[0] + visual.size[0] / 2;
-  const centerY = visual.position[1] + visual.size[1] / 2;
-  const textAttribs = {
-    "text-anchor": "middle",
-    "dominant-baseline": "middle",
-    "font-family": "Helvetica",
-  };
-
-  const textColor = (input: string, _default: string): string => {
-    if (input === "TL") return colorscheme.tlLayerColor;
-    if (input === "TR") return colorscheme.trLayerColor;
-    return _default;
-  };
-
-  return tag(
-    "g",
-    {
-      transform:
-        visual.angle && visual.angle !== 0
-          ? `rotate(${visual.angle}, ${centerX}, ${centerY})`
-          : undefined,
-    },
-    [
-      tag("rect", {
-        width: px(visual.size[0]),
-        height: px(visual.size[1]),
-        x: visual.position[0],
-        y: visual.position[1],
-        fill: colorscheme.keyFill,
-        stroke: colorscheme.keyStroke,
-        "stroke-width": px(2),
-      }),
-      tag(
-        "text",
-        {
-          x: centerX,
-          y: centerY,
-          textLength: px(keySize / 2),
-          fill: textColor(key.main, colorscheme.mainLayerColor),
-          ...textAttribs,
-        },
-        textContents(key.main),
-      ),
-      tag(
-        "text",
-        {
-          x: visual.position[0] + visual.size[0] / 6,
-          y: visual.position[1] + visual.size[1] / 6,
-          fill: textColor(key.tlLayer, colorscheme.tlLayerColor),
-          "font-size": "66%",
-          ...textAttribs,
-        },
-        textContents(key.tlLayer),
-      ),
-      tag(
-        "text",
-        {
-          x: visual.position[0] + (9 * visual.size[0]) / 10,
-          y: visual.position[1] + visual.size[1] / 6,
-          fill: textColor(key.trLayer, colorscheme.trLayerColor),
-          "font-size": "66%",
-          ...textAttribs,
-          "text-anchor": "end",
-        },
-        textContents(key.trLayer),
-      ),
-      tag(
-        "text",
-        {
-          x: visual.position[0] + visual.size[0] / 10,
-          y: visual.position[1] + (5 * visual.size[1]) / 6,
-          fill: textColor(key.blLayer, colorscheme.blLayerColor),
-          "font-size": "66%",
-          ...textAttribs,
-          "text-anchor": "start",
-        },
-        textContents(key.blLayer),
-      ),
-    ].join("\n"),
-  );
-}
-
-function renderLayout(layout: Layout) {
-  return tag(
-    "svg",
-    {
-      viewBox: [
-        -layout.padding,
-        -layout.padding,
-        2 * layout.padding + layout.size[0],
-        2 * layout.padding + layout.size[1],
-      ].join(" "),
-      xmlns: "http://www.w3.org/2000/svg",
-      "xmlns:xlink": "http://www.w3.org/1999/xlink",
-    },
-    layout.visual
-      .map((key, index) =>
-        renderKey(key, layout.keys[index], layout.colorscheme),
-      )
-      .join("\n"),
-  );
-}
-
-const outPath = process.argv[2];
-
-// ========== Layout generation
-const keySize = 50;
-const keySizeVec: Vec2 = [50, 50];
-function visualKey(at: Vec2, angle: number = 0): VisualKey {
-  return { position: at, size: keySizeVec, angle };
-}
-
-function add(x: Vec2, y: Vec2): Vec2 {
-  return [x[0] + y[0], x[1] + y[1]];
-}
-
-function col(at: Vec2): VisualLayout {
-  return [
-    visualKey(at),
-    visualKey(add(at, [0, keySize])),
-    visualKey(add(at, [0, 2 * keySize])),
-  ];
-}
-
-function radians(deg: number): number {
-  return (deg / 180) * Math.PI;
-}
-
-function neg(v: Vec2): Vec2 {
-  return [-v[0], -v[1]];
-}
-
-function thumbs(
-  at: Vec2,
-  reverse: boolean,
-  thumbRotation = reverse ? -15 : 15,
-): VisualLayout {
-  // Distance between thumb key centers
-  const factor = keySize;
-  const offset: Vec2 = [
-    Math.cos(radians(thumbRotation)) * factor,
-    Math.sin(radians(thumbRotation)) * factor,
-  ];
-
-  const result = [
-    visualKey(at, thumbRotation),
-    visualKey(add(at, reverse ? neg(offset) : offset), thumbRotation),
-  ];
-
-  if (reverse) result.reverse();
-
-  return result;
-}
-
-function cols(at: Vec2, cols: Vec2[]): VisualLayout {
-  return cols
-    .map((self, index) => col(add(at, add(self, [index * keySize, 0]))))
-    .flat();
-}
-
-function key(
-  main: string,
-  tlLayer = "",
-  trLayer = "",
-  blLayer = "",
-): KeyboardKey {
-  return { main, tlLayer, trLayer, blLayer };
-}
-
-const block: Vec2[] = [
-  [0, keySize],
-  [0, keySize / 2],
-  [0, 0],
-  [0, keySize / 2],
-  [0, keySize],
-];
-
-const layout: Layout = {
-  colorscheme: {
-    keyFill: "#ffffff",
-    keyStroke: "#000000",
-    mainLayerColor: "black",
-    tlLayerColor: "blue",
-    trLayerColor: "red",
-    blLayerColor: "purple",
-  },
-  visual: [
-    cols([0, 0], block),
-    thumbs([keySize * 3.5, keySize * 4.5], false),
-    thumbs([keySize * 7.5, keySize * 4.5], true),
-    cols([7 * keySize, 0], block),
-  ].flat(),
-  keys: [
-    [
-      key("Q", "!", "1", "f1"),
-      key("A", "&lt;", "6", "f6"),
-      key("X", "&gt;", "", "f11"),
-    ],
-    [
-      key("W", "@", "2", "f2"),
-      key("R", "(", "7", "f7"),
-      key("C", ")", "", "f12"),
-    ],
-    [key("F", "#", "3", "f3"), key("S", "[", "8", "f8"), key("D", "]")],
-    [key("P", "$", "4", "f4"), key("T", "{", "9", "f9"), key("V", "}")],
-    [key("B", "%", "5", "f5"), key("G", "-", "0", "f10"), key("Z", "—")],
-    [key("TR", "", ""), key("␣", "", "")],
-    [key("⇧", "", ""), key("TL", "", "")],
-    [key("J", "^", "🏠"), key("M", "?", "◄", "😱"), key("K", "", "", "🎮")],
-    [
-      key("L", "&amp;", "⏬", "🔊"),
-      key("N", "_", "▼", "🔉"),
-      key("H", "|", "", "🔇"),
-    ],
-    [key("U", "*", "⏫", "🔆"), key("E", "/", "▲", "🔅"), key(",", "\\", "")],
-    [
-      key("Y", "~", "end", "⏪"),
-      key("I", "=", "►", "⏯️"),
-      key(".", "+", "", "⏩"),
-    ],
-    [
-      key(":", "`", "del", "copy"),
-      key("O", ";", "", "paste"),
-      key("'", '"', "", "cut"),
-    ],
-  ].flat(),
-  padding: 20,
-  size: [keySize * 12, keySize * 6],
-};
-
-fs.writeFileSync(outPath, renderLayout(layout));