function createOcclusionMask_cpu(token, intersectingTiles, chunkSize = 3) {
  const maskGraphics = new PIXI.Graphics();
  maskGraphics.beginFill(0xffffff);

  // Get the current scene scale
  const stage = token.mesh.parent;
  const sceneScale = stage?.scale?.x ?? 1;
	
  // Define minimum dimension here so it's available in the entire function scope
  const minDimension = 1 / sceneScale;
	
  // Adjust chunk size based on scene scale
  const adjustedChunkSize = Math.max(chunkSize / sceneScale, 1);

  // Create a single reusable canvas with maximum expected size
  const maxSize = Math.max(
      ...intersectingTiles.map(tile => Math.max(tile.mesh.width, tile.mesh.height))
  );
  const tempCanvas = document.createElement('canvas');
  tempCanvas.width = maxSize;
  tempCanvas.height = maxSize;
  const tempCtx = tempCanvas.getContext('2d', { 
      willReadFrequently: true,
      alpha: true 
  });

  // Pre-allocate typed arrays for better performance
  const tokenBounds = token.mesh.getBounds(false, undefined);
  const rectangles = [];

  for (const tile of intersectingTiles) {
      const tileBounds = tile.mesh.getBounds(false, undefined);
			
      // Calculate intersection area in global space
      const x = Math.max(tokenBounds.x, tileBounds.x);
      const y = Math.max(tokenBounds.y, tileBounds.y);
      const width = Math.min(tokenBounds.x + tokenBounds.width, tileBounds.x + tileBounds.width) - x;
      const height = Math.min(tokenBounds.y + tokenBounds.height, tileBounds.y + tileBounds.height) - y;
			
      // Validate intersection dimensions
      if (width <= 0 || height <= 0) continue;
			
      // Ensure minimum dimensions
      if (width < minDimension || height < minDimension) continue;

      const tileTexture = tile.texture?.baseTexture?.resource?.source;
      if (!tileTexture || tileTexture.width <= 0 || tileTexture.height <= 0) continue;

      // Calculate source and destination rectangles
      const sourceX = (x - tileBounds.x) * (tileTexture.width / tileBounds.width);
      const sourceY = (y - tileBounds.y) * (tileTexture.height / tileBounds.height);
      const sourceWidth = width * (tileTexture.width / tileBounds.width);
      const sourceHeight = height * (tileTexture.height / tileBounds.height);

      // Validate source dimensions
      if (sourceWidth <= 0 || sourceHeight <= 0) continue;
      if (sourceX >= tileTexture.width || sourceY >= tileTexture.height) continue;

      // Calculate actual canvas dimensions needed
      const canvasWidth = Math.ceil(Math.max(1, width));
      const canvasHeight = Math.ceil(Math.max(1, height));

      // Update canvas size if needed
      if (tempCanvas.width < canvasWidth) tempCanvas.width = canvasWidth;
      if (tempCanvas.height < canvasHeight) tempCanvas.height = canvasHeight;

      // Clear only the required portion
      tempCtx.clearRect(0, 0, canvasWidth, canvasHeight);
			
      try {
          // Draw the relevant portion of the tile
          tempCtx.drawImage(tileTexture,
              Math.max(0, sourceX),
              Math.max(0, sourceY),
              Math.min(sourceWidth, tileTexture.width - sourceX),
              Math.min(sourceHeight, tileTexture.height - sourceY),
              0, 0, canvasWidth, canvasHeight
          );

          // Get image data for the intersection area
          const imageData = tempCtx.getImageData(0, 0, canvasWidth, canvasHeight);
          const dataU32 = new Uint32Array(imageData.data.buffer);

          // Process chunks in world space
          const chunksX = Math.ceil(width / adjustedChunkSize);
          const chunksY = Math.ceil(height / adjustedChunkSize);

          for (let cy = 0; cy < chunksY; cy++) {
              const yPos = cy * adjustedChunkSize;
              const chunkHeight = Math.min(adjustedChunkSize, height - yPos);

              for (let cx = 0; cx < chunksX; cx++) {
                  const xPos = cx * adjustedChunkSize;
                  const chunkWidth = Math.min(adjustedChunkSize, width - xPos);
									
                  // Convert chunk position to texture space
                  const textureX = Math.floor(xPos * (canvasWidth / width));
                  const textureY = Math.floor(yPos * (canvasHeight / height));
                  const textureWidth = Math.ceil(chunkWidth * (canvasWidth / width));
                  const textureHeight = Math.ceil(chunkHeight * (canvasHeight / height));

                  // Validate texture coordinates
                  if (textureX >= canvasWidth || textureY >= canvasHeight) continue;
                  if (textureWidth <= 0 || textureHeight <= 0) continue;

                  // Check if chunk has any non-transparent pixels
                  let hasOpaquePixel = false;
									
                  for (let py = textureY; py < Math.min(textureY + textureHeight, canvasHeight); py++) {
                      const rowOffset = py * canvasWidth;
                      for (let px = textureX; px < Math.min(textureX + textureWidth, canvasWidth); px++) {
                          if ((dataU32[rowOffset + px] >>> 24) > 0) {
                              hasOpaquePixel = true;
                              rectangles.push({
                                  x: x + xPos,
                                  y: y + yPos,
                                  width: chunkWidth,
                                  height: chunkHeight
                              });
                              py = canvasHeight; // Break outer loop
                              break;
                          }
                      }
                  }
              }
          }
      } catch (error) {
          console.warn('Error processing tile:', error);
          continue;
      }
  }

  // Apply matrix transform to graphics to handle zoom
  maskGraphics.transform.setFromMatrix(stage.transform.worldTransform);

  // Draw all rectangles individually
  for (const rect of rectangles) {
      // Convert rectangle coordinates to local space and ensure minimum dimensions
      const localX = rect.x / sceneScale;
      const localY = rect.y / sceneScale;
      const localWidth = Math.max(rect.width / sceneScale, minDimension);
      const localHeight = Math.max(rect.height / sceneScale, minDimension);
			
      maskGraphics.drawRect(localX, localY, localWidth, localHeight);
  }

  // Reset transform
  maskGraphics.transform.setFromMatrix(new PIXI.Matrix());

  // Clean up
  tempCanvas.remove();
  return maskGraphics;
}