When generating tiles for websites, designers and developers often encounter issues when rotating tiles. Common solutions involve less-than-ideal hacks, such as adding pseudoelements, scaling, and rotating them to achieve the desired effect. These workarounds can complicate code, reduce performance, and make maintenance harder.
WTC Tile Maker aims to solve this by providing a robust, programmatic solution for generating and manipulating tiles, including rotation, without relying on CSS hacks.
Generate a seamlessly tileable rotated version of your image:
# Rotate an image to 45 degrees
deno run -A main.ts generate -d 45 input.png
# Or use a preset angle index (run 'list' to see all options)
deno run -A main.ts generate -a 3 input.png
# List all available rational angles
deno run -A main.ts list
# Help
deno run -A main.ts help
The output file will be saved in the same directory as the input with the angle appended to the filename (e.g., input-tile-45.png).
- Clone the repository:
git clone https://github.com/wethegit/wtc-tile-maker-deno-sharp.git cd wtc-tile-maker-deno-sharp - Run the main script:
deno run -A main.ts
| Command | Description |
|---|---|
help |
Display the help message |
list |
List available rational angles |
generate |
Generate a tile pattern from an input image |
version |
Show version information |
| Option | Description |
|---|---|
-a, --angleOption |
Index of rational angle to use (default: 0) |
-d, --degrees |
Desired angle in degrees (overrides angleOption) |
-o, --output |
Output file name (default: derived from input) |
-s, --maxValidSize |
Maximum valid dimension for output tiles (default: 2000) |
-l, --allowLargeBuffers |
Allow processing of large image buffers (default: false) |
-q, --quality |
Quality of the output image (default: 90) |
-m, --tileMargin |
Margin around tiles (default: 1) |
-v, --verbose |
Enable verbose output (default: false) |
These are the angles where tan(θ) = m/n for small integers m, n. These angles produce periodic tilings when used for rotation.
| Index | Label | m | n |
|---|---|---|---|
| 0 | 0° | 0 | 1 |
| 1 | 90° | 1 | 0 |
| 2 | -90° | -1 | 0 |
| 3 | 45° | 1 | 1 |
| 4 | -45° | -1 | 1 |
| 5 | 26.565° (arctan 1/2) | 1 | 2 |
| 6 | -26.565° (arctan -1/2) | -1 | 2 |
| 7 | 63.435° (arctan 2) | 2 | 1 |
| 8 | -63.435° (arctan -2) | -2 | 1 |
| 9 | 18.435° (arctan 1/3) | 1 | 3 |
| 10 | -18.435° (arctan -1/3) | -1 | 3 |
| 11 | 71.565° (arctan 3) | 3 | 1 |
| 12 | -71.565° (arctan -3) | -3 | 1 |
| 13 | 14.036° (arctan 1/4) | 1 | 4 |
| 14 | -14.036° (arctan -1/4) | -1 | 4 |
| 15 | 75.964° (arctan 4) | 4 | 1 |
| 16 | -75.964° (arctan -4) | -4 | 1 |
| 17 | 33.690° (arctan 2/3) | 2 | 3 |
| 18 | -33.690° (arctan -2/3) | -2 | 3 |
| 19 | 56.310° (arctan 3/2) | 3 | 2 |
| 20 | -56.310° (arctan -3/2) | -3 | 2 |
| 21 | 36.870° (arctan 3/4) | 3 | 4 |
| 22 | -36.870° (arctan -3/4) | -3 | 4 |
| 23 | 53.130° (arctan 4/3) | 4 | 3 |
| 24 | -53.130° (arctan -4/3) | -4 | 3 |
| 25 | 11.310° (arctan 1/5) | 1 | 5 |
| 26 | -11.310° (arctan -1/5) | -1 | 5 |
| 27 | 78.690° (arctan 5) | 5 | 1 |
| 28 | -78.690° (arctan -5) | -5 | 1 |
You can also run deno -A main.ts list to see these angles.
The output tile size depends on how well the input dimensions align with the m:n ratios of the angles. For best results, use these aspect ratios:
| Aspect Ratio | Works Best With Angles | Notes |
|---|---|---|
| 1:1 (square) | All angles | Always produces predictable, symmetrical outputs |
| 2:1 or 1:2 | arctan(1/2), arctan(2) | Aligns perfectly with 26.565° and 63.435° |
| 3:2 or 2:3 | arctan(2/3), arctan(3/2) | Good for 33.69° and 56.31°; common photo ratio |
| 4:3 or 3:4 | arctan(3/4), arctan(4/3) | Good for 36.87° and 53.13°; classic screen ratio |
| 3:1 or 1:3 | arctan(1/3), arctan(3) | Good for 18.435° and 71.565° |
| 4:1 or 1:4 | arctan(1/4), arctan(4) | Good for 14.036° and 75.964° |
| 5:1 or 1:5 | arctan(1/5), arctan(5) | Good for 11.31° and 78.69° |
Aspect ratios with prime numbers > 5 (like 7:3, 11:4, 13:8) or irrational proportions will produce larger outputs because the GCD calculations yield smaller divisors.
deno -A main.ts helpdeno -A main.ts listGenerate a rotated tile from Checker.png using angle index 27, and max size 6000:
This will also work with images of different aspect ratios (like 3×2). For example. this enerates a rotated tile from 3x2-checker.png using 45°:
N.B. Notice, here, how there seems to be a piece missing! This is because the default tile margin is too small to make these rotated tiles cover the output dimensions. This command should be updated to generate -m 2 -d 45 3x2-checker.png.
Sometimes, a combination of input size and rotation will produce an output that is either too large or creating an appropriate output tile is just beyong the capabilities of this math. In this case you should likely fall back to hacky methods or get a tile produces in a more predictable aspect ratio.
If you want to specify an angle in degrees instead of an index:
deno -A main.ts generate -d 45 Checker.pngThe tool will find the closest rational angle and generate the tile accordingly.
- allowLargeBuffers: If you receive an error about
allowLargeBuffers, use the-lflag. Warning: this may affect performance, but enables processing of narrow rotation / large image combinations. - Missing pieces: If the output appears to be missing pieces around the edges, try increasing the
-m(tileMargin) value, e.g.,-m 3.
When you rotate a regular tiling pattern (like a checkerboard) by an arbitrary angle, the result doesn't tile seamlessly in the x and y direction anymore.
The key insight is that rational angles—angles where tan(θ) = m/n for small integers m and n—produce periodic tilings when used for rotation. At these specific angles, the rotated grid aligns back to integer positions, allowing seamless repetition.
For example:
tan(45°) = 1/1→ m=1, n=1tan(26.565°) = 1/2→ m=1, n=2tan(63.435°) = 2/1→ m=2, n=1
For a rational angle with tan(θ) = m/n, the rotated pattern repeats at intervals of √(m² + n²) times the original period.
To create a seamlessly tileable output:
- The output dimensions are calculated as:
input dimensions × √(m² + n²) / gcd(m, n) - This ensures the rotated grid aligns back to integer positions
- Input: A tileable source image (e.g., a checkerboard pattern)
- Tile: The source image is tiled into a larger canvas to provide enough material for the rotation
- Rotate: The tiled canvas is rotated by the selected rational angle
- Crop: A precisely calculated region is extracted that will tile seamlessly
- Output: The resulting image can be used as a CSS background and will tile perfectly at the rotated angle
This approach eliminates the need for CSS hacks like pseudoelements with transform: rotate() and scale(), resulting in cleaner code, better performance, and more predictable rendering across browsers.



