System 3LD6 Project

System-3LD6 is a multi-layered generative art system that builds geometric compositions using the randomness of D6 dice rolls. Three distinct layers shape each piece: sector layout, sector color, and sector depth. Following a predefined set of rules, the outcomes of successive dice rolls determine how sectors appear, interact, and evolve within the grid—gradually forming the final image. At its core, the composition resembles a top-down isometric view of a 3D scene—though no actual three-dimensional model exists. The illusion of depth emerges through careful shading and dithering, a digital echo of hand-drawn sketches and paper experiments.

System 3LD6 Detail 1
System 3LD6 Detail 2

Core Algorithm

The system operates through several interconnected stages:

// STAGE 1: GRID CONSTRUCTION
function initializeSystem(dimensions, gridSize) {
  canvas = createCanvas(dimensions)
  grid = createRegularGrid(gridSize, margins)
  dots = generateGridPoints(grid)
  lines = connectAdjacentDots(dots)
  sectors = defineCellsBetweenLines(dots, lines)
  return {grid, dots, lines, sectors}
}

// STAGE 2: RANDOM WALK PATHFINDING (1st D6 - WALKS)
function performRandomWalks(numWalks, grid) {
  for each walk in numWalks:
    walker = createWalker(centerPosition)
    
    while not reachedBoundary:
      diceRoll = rollD6() // D6 dice determines direction & steps
      // 1-3: move backward/left (1,2,3 steps), 4-6: move forward/right (1,2,3 steps)
      direction = (diceRoll <= 3) ? -1 : 1
      stepCount = ((diceRoll - 1) % 3) + 1
      
      for step in stepCount:
        walker.moveInDirection(direction, currentOrientation)
        walker.alternateOrientation() // vertical ↔ horizontal
        if walker.reachedBoundary(): break
        
    // Impact line thickness based on walker path
    for each pathSegment in walker.path:
      correspondingLine = findLineByEndpoints(pathSegment)
      correspondingLine.increaseThickness(adaptValue)
}

// STAGE 3: REGION FORMATION (2nd & 3rd D6 - COLORS & DEPTHS)
function createRegions(sectors, lines) {
  for each sector in sectors:
    // Check if sector boundaries are "open" or "closed"
    sector.bounds = checkLineThickness(sector.borderLines)
    
  // Group connected sectors using flood fill
  for each unprocessedSector:
    // 2nd D6: Color assignment (1-2→color1, 3-4→color2, 5-6→color3)
    colorDice = rollD6()
    randomColor = selectColorFromPalette(colorDice)
    floodFillColor(sector, randomColor, openBoundaries)
    
    // 3rd D6: Depth assignment (1→-2, 2→-1, 3-4→0, 5→+1, 6→+2)
    depthDice = rollD6()
    randomDepth = mapDiceToDepth(depthDice) // -2 to +2 range
    floodFillDepth(sector, randomDepth, openBoundaries)
}

// STAGE 4: 3D VISUALIZATION & SHADING
function generateShading(sectors) {
  for each sector in sectors:
    // Determine shading based on depth relationships
    neighborDepths = getNeighborDepths(sector)
    
    if sector.depth < neighborDepths.value:
      sector.addShadowEdge(edgeDirection)
      shadeType = calculateShadeShape(neighborComparison)
      
    // Apply gradient-based shading
    applyVerticalGradient(sector) if hasTopShadow
    applyHorizontalGradient(sector) if hasRightShadow  
    applyRadialGradient(sector) if hasCornerShadow
}

// STAGE 5: POST-PROCESSING EFFECTS
function applyDithering(canvas, parameters) {
  imageData = extractGrayscaleData(canvas)
  
  for each pixel in imageData:
    noiseValue = perlinNoise(pixelPosition, noiseScale)
    
    if noiseValue > threshold:
      circleRadius = mapToRange(noiseValue, minRadius, maxRadius)
      drawCircle(pixelPosition, circleRadius, ditherColor)
      
    // Apply Floyd-Steinberg error diffusion
    quantizedValue = quantizeToLevels(pixel, levelCount)
    error = pixel - quantizedValue
    distributeErrorToNeighbors(error)
}

// MAIN EXECUTION - "3LD6" SYSTEM
function generateArtwork() {
  system = initializeSystem(3000x3000, gridWidth)
  
  // Three layers of D6-based randomness give the system its name:
  performRandomWalks(10, system.grid)      // 1st D6: Walk directions
  createRegions(system.sectors, system.lines) // 2nd D6: Colors, 3rd D6: Depths
  generateShading(system.sectors)
  
  // Multi-layer output
  backgroundLayer = renderBackground()
  sectorLayer = renderColoredSectors()  
  shadingLayer = renderShading()
  lineLayer = renderGridLines()
  
  applyDithering(shadingLayer)
  applyDithering(lineLayer)
  
  return combineLayersToFinalImage()
}

Three Dice Layers

  • 1st D6 - Walk Directions: Controls pathfinding movement (1-3: alternating backward, left; 4-6: alternating forward, right)
  • 2nd D6 - Sector Colors: Assigns palette colors (1-2, 3-4, 5-6 map to 3 colors)
  • 3rd D6 - Depth Values: Creates elevation layers (1→-2, 2→-1, 3-4→0, 5→+1, 6→+2)

Multi-Layer Output

The system generates five separate image layers that can be used independently or combined, for example for screen-printing purposes:

  • MAIN: Final composite image with all effects
  • BG: Clean background layer
  • SEC: Colored sectors without shading
  • SHAD: Shading and dithering effects only
  • LIN: Grid lines and structural elements