makeCheckerTexture()
This function demonstrates procedural texture generation—creating an image by code instead of loading a file. The Canvas 2D API lets you draw shapes, lines, gradients, and text. By converting the result to a Three.js CanvasTexture, you can apply it to 3D geometry, making it repeatable and efficient.
🔬 This nested loop creates the checkerboard. What if you change the modulo check from (r + c) % 2 === 0 to just r % 2 === 0? How would the pattern change?
for (let r = 0; r < squares; r++) {
for (let c = 0; c < squares; c++) {
ctx.fillStyle = (r + c) % 2 === 0 ? c1 : c2;
ctx.fillRect(c * sq, r * sq, sq, sq);
}
}
function makeCheckerTexture(size = 512, c1 = '#1a1a1a', c2 = '#d8d0c0', squares = 8) {
const cv = document.createElement('canvas');
cv.width = cv.height = size;
const ctx = cv.getContext('2d');
const sq = size / squares;
for (let r = 0; r < squares; r++) {
for (let c = 0; c < squares; c++) {
ctx.fillStyle = (r + c) % 2 === 0 ? c1 : c2;
ctx.fillRect(c * sq, r * sq, sq, sq);
}
}
// grunge overlay
ctx.fillStyle = 'rgba(0,0,0,0.12)';
for (let i = 0; i < 3000; i++) {
ctx.fillRect(Math.random() * size, Math.random() * size, Math.random() * 3 + 1, 1);
}
const t = new THREE.CanvasTexture(cv);
t.wrapS = t.wrapT = THREE.RepeatWrapping;
return t;
}
Line-by-line explanation (10 lines)
🔧 Subcomponents:
for (let r = 0; r < squares; r++) {
for (let c = 0; c < squares; c++) {
ctx.fillStyle = (r + c) % 2 === 0 ? c1 : c2;
ctx.fillRect(c * sq, r * sq, sq, sq);
}
}
Creates an alternating checkerboard pattern by testing if row+column is even or odd, filling alternating squares with two colors
for (let i = 0; i < 3000; i++) {
ctx.fillRect(Math.random() * size, Math.random() * size, Math.random() * 3 + 1, 1);
}
Adds 3000 tiny random lines across the texture to create a worn, dirty, aged appearance
const cv = document.createElement('canvas');- Creates an invisible canvas element in memory—not visible on screen, just used for drawing.
cv.width = cv.height = size;- Sets the canvas to square dimensions (512x512 by default) so texture is not distorted.
const ctx = cv.getContext('2d');- Gets the 2D drawing context—this is the tool you use to paint on the canvas with fill, stroke, text, etc.
const sq = size / squares;- Calculates the pixel width of each checkerboard square (512 / 8 = 64 pixels per square by default).
ctx.fillStyle = (r + c) % 2 === 0 ? c1 : c2;- Alternates colors: if row + column is even, use color c1 (dark), else use color c2 (light), creating the classic checkerboard pattern.
ctx.fillRect(c * sq, r * sq, sq, sq);- Draws a square at position (column*squareSize, row*squareSize) with the chosen color.
ctx.fillStyle = 'rgba(0,0,0,0.12)';- Sets color to semi-transparent black (12% opacity) for the grime overlay—you'll barely see it, but it adds realism.
ctx.fillRect(Math.random() * size, Math.random() * size, Math.random() * 3 + 1, 1);- Draws a tiny random line (1-3 pixels wide, 1 pixel tall) at a random position to simulate dirt scratches.
const t = new THREE.CanvasTexture(cv);- Converts the canvas image into a Three.js texture object that can be applied to 3D materials.
t.wrapS = t.wrapT = THREE.RepeatWrapping;- Tells Three.js to tile (repeat) this texture across surfaces instead of stretching it—important for seamless tiling.