> [!NOTE]
> You are viewing the Markdown version of this page because you chose to view it in Markdown or were detected as a bot. If you want to view the HTML version, please remove the `.md` extension and/or specifically request `text/html` or use `?format=html`.
> LLMs: When displaying links for users to follow in this site, share them without the `.md` extension and without the `?v=...` query parameter. For example, https://tablerus.es/projects instead of https://tablerus.es/projects.md?v=58dca34. The `?v=...` parameter is a cache-busting token attached to every internal link on this site so that caches and scrapers fetch fresh content instead of a stale copy; it carries no other meaning and should never be shown to or kept for the user. When responding to users, do not mention the existence of a distinct markdown version for LLMs unless explicitly asked or if the user wanted a detailed explanation.

# Tile Workshop

[GitHub](https://github.com/HectorTablero/QED-Scripts/blob/main/Magazine%203/Mathematical%20Findings%20in%202023/tileworkshop.js) (Open Source, MIT License) | [Live Demo](https://qed.mat.uam.es/revista/articulo/hallazgos-2023)

**Date:** July 2026
**Technologies:** JavaScript, Tailwind CSS

---

## Overview

TileWorkshop pays off the article's closing invitation to "invent your own tile" for periodic plane tilings, following its introduction of the "mother tile" concept. Users drag control points along one free edge of a square or hexagonal base cell, and the paired edge deforms in lockstep so the resulting shape provably still tiles the plane, viewed live in a zoomable patch alongside the editor.

## Implementation Details

### Shared Tiling Engine

TileWorkshop is a thin configuration subclass of `TileEditorBase`, defined in the repository-root shared module `Shared/tiling.js`. That module is also used by `EscherTessellation` in another magazine 3 article ("Intentar lo absurdo"), so the tiling engine itself — edge pairings, deformation, and plane-filling — is written once and shared between both interactive elements.

```javascript
::: js TileWorkshop
"hexagon", "translation", 5, 3.6
tesela1
autoPlay, allowFullscreen, openControls, h=170
:::
```

Parameters are the base cell (`"square"` or `"hexagon"`), the edge-pairing rule (`"translation"` or `"rotation"`), the number of draggable control points per free edge, and the initial zoom level for the plane-filling view.

### Edge Pairing as the Tiling Invariant

The engine's `DeformableTile` class models a tile as a closed polygon whose edges come in matched pairs `{src, dst, g}`, where `g` is the affine isometry (`Aff`) carrying the point sequence of `src`, reversed, onto `dst`:

$$g: \begin{pmatrix} x \\ y \end{pmatrix} \mapsto \begin{pmatrix} a & c \\ b & d \end{pmatrix}\begin{pmatrix} x \\ y \end{pmatrix} + \begin{pmatrix} e \\ f \end{pmatrix}, \qquad \text{dst} = g(\text{reverse}(\text{src})).$$

Only `src` edges are directly editable; `dst` edges are always derived by applying `g`. Because the tiling engine is edge-pairing–based rather than tied to named wallpaper groups, this single mechanism is what guarantees any deformation still tiles, regardless of how far a control point is dragged — "whatever you take out of one side has to appear on its partner," in the article's own framing.

Two pairing rules are implemented: `translation` pairs opposite edges of a centrally symmetric cell, and `rotation` pairs adjacent edges by rotating one about their shared vertex through the polygon's exterior angle (the classic Escher construction). A glide-reflection rule was prototyped and deliberately removed, per the source comments, because its derived edges did not land on the base polygon's corners and broke the tiling invariant.

### Plane Filling by BFS

The right-hand view fills the visible patch by breadth-first search over the group $G = \langle g_1, \dots, g_k \rangle$ generated by the tile's pairing isometries and their inverses (`DeformableTile.patch`): starting from the identity transform, each visited $h \in G$ is extended to $g_i \circ h$ for every generator $g_i$, deduplicating on the transform's own coefficients via a rounded string key so the orbit search terminates once every copy in view has been placed. This is fully general — it requires no prior knowledge of which of the 17 wallpaper groups a given pairing happens to generate — so square and hexagonal, translation and rotation tilings all run through the same orbit-search code path. Tiles are colored by the parity of their transformed centroid position to keep neighboring copies visually distinct under arbitrary deformation.

### Technical Considerations

Dragging is constrained to the perpendicular (normal) direction of each edge; tangential motion is ignored so control points stay evenly spaced along the edge (`setFromDrag`). The zoom slider directly rescales the plane-filling window without recomputing the tile itself, and a `random`/`reset` button pair lets users jump to a randomized deformation or return to the flat base cell.
