export function createVoxelTunnel(THREE, config, scene, pointLights = []) {
  const count = config.count || 100000;
  const tunnelRadius = config.tunnelRadius || 8;
  const tunnelLength = config.tunnelLength || 300;
  const tunnelOffsetZ = config.tunnelOffsetZ || 0;
  const voxelSize = config.voxelSize || 0.25;
  const sizeVariation = config.voxelSizeVariation || 0.1;
  const voxelGap = config.voxelGap || 0.05;
  const density = config.density || 0.8;
  
  // Displacement settings
  const dispStrength = config.displacement?.strength || 1.0;
  const dispFrequency = config.displacement?.frequency || 1.0;
  const dispSpeed = config.displacement?.speed || 0.3;
  const dispNoiseScale = config.displacement?.noiseScale || 1.5;
  const waveAmplitude = config.displacement?.waveAmplitude || 0.5;
  const waveFrequency = config.displacement?.waveFrequency || 3.0;
  
  // Animation settings
  const pulseSpeed = config.animation?.pulseSpeed || 0.5;
  const pulseStrength = config.animation?.pulseStrength || 0.2;
  const rotateSpeed = config.animation?.rotateSpeed || 0.05;
  const flowSpeed = config.animation?.flowSpeed || 0.2;
  
  // Color settings
  const colorInner = new THREE.Color(config.colors?.inner || '#00ffff');
  const colorOuter = new THREE.Color(config.colors?.outer || '#0044aa');
  const emissiveInner = new THREE.Color(config.colors?.emissiveInner || '#00aaff');
  const emissiveOuter = new THREE.Color(config.colors?.emissiveOuter || '#002255');
  const emissiveIntensity = config.colors?.emissiveIntensity || 1.0;
  const colorVariation = config.colors?.variation || 0.2;
  const hueShift = config.colors?.hueShift || 0.1;
  const saturation = config.colors?.saturation || 1.0;
  const brightness = config.colors?.brightness || 1.0;
  
  // Glow settings
  const glowStrength = config.glow?.strength || 0.5;
  const glowFalloff = config.glow?.falloff || 2.0;
  
  // Material settings (metallic/reflective)
  const metalness = config.material?.metalness || 0.7;
  const roughness = config.material?.roughness || 0.3;
  const reflectivity = config.material?.reflectivity || 0.8;
  const envMapIntensity = config.material?.envMapIntensity || 1.0;
  const fresnelPower = config.material?.fresnelPower || 3.0;
  const envMapRotation = config.material?.envMapRotation || 0.0;
  const specularPower = config.material?.specularPower || 32.0;
  const specularIntensity = config.material?.specularIntensity || 1.0;
  
  // Load environment map
  const textureLoader = new THREE.TextureLoader();
  const envMapTexture = textureLoader.load(
    'images/sphere-stars.jpg',
    (tex) => { console.log('Voxel env map loaded'); },
    undefined,
    (err) => { console.warn('Failed to load voxel env map, using fallback'); }
  );
  envMapTexture.mapping = THREE.EquirectangularReflectionMapping;

  // Create dodecahedron geometry for each voxel (looks great with reflections)
  const voxelGeo = new THREE.DodecahedronGeometry(voxelSize * 0.6, 0);
  
  // Create shader material for voxels
  const voxelMaterial = new THREE.ShaderMaterial({
    uniforms: {
      uTime: { value: 0 },
      uEnvMap: { value: envMapTexture },
      uColorInner: { value: colorInner },
      uColorOuter: { value: colorOuter },
      uEmissiveInner: { value: emissiveInner },
      uEmissiveOuter: { value: emissiveOuter },
      uEmissiveIntensity: { value: emissiveIntensity },
      uDispStrength: { value: dispStrength },
      uDispFrequency: { value: dispFrequency },
      uDispSpeed: { value: dispSpeed },
      uDispNoiseScale: { value: dispNoiseScale },
      uWaveAmplitude: { value: waveAmplitude },
      uWaveFrequency: { value: waveFrequency },
      uPulseSpeed: { value: pulseSpeed },
      uPulseStrength: { value: pulseStrength },
      uRotateSpeed: { value: rotateSpeed },
      uFlowSpeed: { value: flowSpeed },
      uHueShift: { value: hueShift },
      uSaturation: { value: saturation },
      uBrightness: { value: brightness },
      uGlowStrength: { value: glowStrength },
      uGlowFalloff: { value: glowFalloff },
      uTunnelRadius: { value: tunnelRadius },
      uMetalness: { value: metalness },
      uRoughness: { value: roughness },
      uReflectivity: { value: reflectivity },
      uEnvMapIntensity: { value: envMapIntensity },
      uFresnelPower: { value: fresnelPower },
      uEnvMapRotation: { value: envMapRotation },
      uSpecularPower: { value: specularPower },
      uSpecularIntensity: { value: specularIntensity },
      uVoxelRotSpeed: { value: config.animation?.voxelRotSpeed || 1.0 },
      uFogColor: { value: new THREE.Color(0x041008) },
      uFogNear: { value: 30 },
      uFogFar: { value: 180 },
      // Dynamic point lights - initialized from scene data
      uPointLight1Pos: { value: new THREE.Vector3(
        pointLights[0]?.position?.x || 0, 
        pointLights[0]?.position?.y || 0, 
        pointLights[0]?.animation?.startZ || 50
      ) },
      uPointLight1Color: { value: new THREE.Color(pointLights[0]?.color || 0xff4444) },
      uPointLight1Intensity: { value: pointLights[0]?.intensity || 5.0 },
      uPointLight1Distance: { value: pointLights[0]?.distance || 80 },
      uPointLight2Pos: { value: new THREE.Vector3(
        pointLights[1]?.position?.x || 0, 
        pointLights[1]?.position?.y || 0, 
        pointLights[1]?.animation?.startZ || -100
      ) },
      uPointLight2Color: { value: new THREE.Color(pointLights[1]?.color || 0x4466ff) },
      uPointLight2Intensity: { value: pointLights[1]?.intensity || 5.0 },
      uPointLight2Distance: { value: pointLights[1]?.distance || 80 },
    },
    vertexShader: `
      attribute vec3 instanceOffset;
      attribute float instanceScale;
      attribute float instancePhase;
      attribute float instanceRingT;
      attribute vec3 instanceNormal;
      attribute vec3 instanceRotAxis;
      attribute float instanceRotSpeed;
      
      uniform float uTime;
      uniform float uDispStrength;
      uniform float uDispFrequency;
      uniform float uDispSpeed;
      uniform float uDispNoiseScale;
      uniform float uWaveAmplitude;
      uniform float uWaveFrequency;
      uniform float uPulseSpeed;
      uniform float uPulseStrength;
      uniform float uRotateSpeed;
      uniform float uFlowSpeed;
      uniform float uTunnelRadius;
      uniform float uVoxelRotSpeed;
      
      varying vec3 vNormal;
      varying vec3 vWorldPos;
      varying vec3 vViewDir;
      varying float vRingT;
      varying float vPhase;
      varying float vDepth;
      
      // Simplex noise for displacement
      vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
      vec4 mod289(vec4 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
      vec4 permute(vec4 x) { return mod289(((x*34.0)+1.0)*x); }
      vec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; }
      
      float snoise(vec3 v) {
        const vec2 C = vec2(1.0/6.0, 1.0/3.0);
        const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
        
        vec3 i = floor(v + dot(v, C.yyy));
        vec3 x0 = v - i + dot(i, C.xxx);
        
        vec3 g = step(x0.yzx, x0.xyz);
        vec3 l = 1.0 - g;
        vec3 i1 = min(g.xyz, l.zxy);
        vec3 i2 = max(g.xyz, l.zxy);
        
        vec3 x1 = x0 - i1 + C.xxx;
        vec3 x2 = x0 - i2 + C.yyy;
        vec3 x3 = x0 - D.yyy;
        
        i = mod289(i);
        vec4 p = permute(permute(permute(
          i.z + vec4(0.0, i1.z, i2.z, 1.0))
          + i.y + vec4(0.0, i1.y, i2.y, 1.0))
          + i.x + vec4(0.0, i1.x, i2.x, 1.0));
        
        float n_ = 0.142857142857;
        vec3 ns = n_ * D.wyz - D.xzx;
        
        vec4 j = p - 49.0 * floor(p * ns.z * ns.z);
        
        vec4 x_ = floor(j * ns.z);
        vec4 y_ = floor(j - 7.0 * x_);
        
        vec4 x = x_ * ns.x + ns.yyyy;
        vec4 y = y_ * ns.x + ns.yyyy;
        vec4 h = 1.0 - abs(x) - abs(y);
        
        vec4 b0 = vec4(x.xy, y.xy);
        vec4 b1 = vec4(x.zw, y.zw);
        
        vec4 s0 = floor(b0) * 2.0 + 1.0;
        vec4 s1 = floor(b1) * 2.0 + 1.0;
        vec4 sh = -step(h, vec4(0.0));
        
        vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy;
        vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww;
        
        vec3 p0 = vec3(a0.xy, h.x);
        vec3 p1 = vec3(a0.zw, h.y);
        vec3 p2 = vec3(a1.xy, h.z);
        vec3 p3 = vec3(a1.zw, h.w);
        
        vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2,p2), dot(p3,p3)));
        p0 *= norm.x; p1 *= norm.y; p2 *= norm.z; p3 *= norm.w;
        
        vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
        m = m * m;
        return 42.0 * dot(m*m, vec4(dot(p0,x0), dot(p1,x1), dot(p2,x2), dot(p3,x3)));
      }
      
      // Rodrigues' rotation formula
      vec3 rotateAxis(vec3 p, vec3 axis, float angle) {
        float c = cos(angle);
        float s = sin(angle);
        return p * c + cross(axis, p) * s + axis * dot(axis, p) * (1.0 - c);
      }
      
      void main() {
        vRingT = instanceRingT;
        vPhase = instancePhase;
        
        // Rotate the vertex around instance's rotation axis
        float rotAngle = uTime * instanceRotSpeed * uVoxelRotSpeed + instancePhase * 6.28;
        vec3 rotatedPos = rotateAxis(position, normalize(instanceRotAxis), rotAngle);
        vec3 rotatedNorm = rotateAxis(normal, normalize(instanceRotAxis), rotAngle);
        
        // Base position from instance
        vec3 basePos = instanceOffset;
        
        // Apply noise-based displacement
        vec3 noisePos = basePos * uDispNoiseScale * uDispFrequency * 0.1;
        noisePos.z += uTime * uDispSpeed;
        
        float noise1 = snoise(noisePos) * 0.5;
        float noise2 = snoise(noisePos * 2.0 + 100.0) * 0.25;
        float noise3 = snoise(noisePos * 4.0 + 200.0) * 0.125;
        float totalNoise = noise1 + noise2 + noise3;
        
        // Wave displacement along tunnel
        float wave1 = sin(basePos.z * uWaveFrequency * 0.05 + uTime * uDispSpeed * 2.0) * uWaveAmplitude;
        float wave2 = sin(basePos.z * uWaveFrequency * 0.08 + uTime * uDispSpeed * 1.5 + instancePhase) * uWaveAmplitude * 0.5;
        
        // Radial pulse
        float pulse = sin(uTime * uPulseSpeed + instancePhase * 3.0) * uPulseStrength;
        
        // Flow along tunnel (rotation around center)
        float flowAngle = uTime * uFlowSpeed + basePos.z * 0.01;
        
        // Combine all displacements
        float totalDisp = totalNoise * uDispStrength + wave1 + wave2 + pulse;
        
        // Displace along the normal (toward/away from center)
        vec3 displacedPos = basePos + instanceNormal * totalDisp;
        
        // Apply slight rotation around tunnel axis
        float tunnelRotAngle = uTime * uRotateSpeed + instancePhase * 0.5;
        float cosR = cos(tunnelRotAngle * 0.2);
        float sinR = sin(tunnelRotAngle * 0.2);
        vec2 rotXY = vec2(
          displacedPos.x * cosR - displacedPos.y * sinR,
          displacedPos.x * sinR + displacedPos.y * cosR
        );
        displacedPos.xy = rotXY;
        
        // Scale variation with pulse
        float scale = instanceScale * (1.0 + pulse * 0.5);
        
        // Transform vertex (using rotated position)
        vec3 transformed = rotatedPos * scale + displacedPos;
        
        vec4 mvPosition = modelViewMatrix * vec4(transformed, 1.0);
        gl_Position = projectionMatrix * mvPosition;
        
        vNormal = normalize(normalMatrix * rotatedNorm);
        vWorldPos = transformed;
        vViewDir = normalize(-mvPosition.xyz);
        vDepth = -mvPosition.z;
      }
    `,
    fragmentShader: `
      uniform sampler2D uEnvMap;
      uniform vec3 uColorInner;
      uniform vec3 uColorOuter;
      uniform vec3 uEmissiveInner;
      uniform vec3 uEmissiveOuter;
      uniform float uEmissiveIntensity;
      uniform float uTime;
      uniform float uHueShift;
      uniform float uSaturation;
      uniform float uBrightness;
      uniform float uGlowStrength;
      uniform float uGlowFalloff;
      uniform float uTunnelRadius;
      uniform float uMetalness;
      uniform float uRoughness;
      uniform float uReflectivity;
      uniform float uEnvMapIntensity;
      uniform float uFresnelPower;
      uniform float uEnvMapRotation;
      uniform float uSpecularPower;
      uniform float uSpecularIntensity;
      uniform vec3 uFogColor;
      uniform float uFogNear;
      uniform float uFogFar;
      
      // Dynamic point lights
      uniform vec3 uPointLight1Pos;
      uniform vec3 uPointLight1Color;
      uniform float uPointLight1Intensity;
      uniform float uPointLight1Distance;
      uniform vec3 uPointLight2Pos;
      uniform vec3 uPointLight2Color;
      uniform float uPointLight2Intensity;
      uniform float uPointLight2Distance;
      
      varying vec3 vNormal;
      varying vec3 vWorldPos;
      varying vec3 vViewDir;
      varying float vRingT;
      varying float vPhase;
      varying float vDepth;
      
      #define PI 3.14159265359
      
      // HSV conversion for color effects
      vec3 rgb2hsv(vec3 c) {
        vec4 K = vec4(0.0, -1.0/3.0, 2.0/3.0, -1.0);
        vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
        vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
        float d = q.x - min(q.w, q.y);
        float e = 1.0e-10;
        return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
      }
      
      vec3 hsv2rgb(vec3 c) {
        vec4 K = vec4(1.0, 2.0/3.0, 1.0/3.0, 3.0);
        vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
        return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
      }
      
      // Sample environment map using equirectangular projection
      vec3 sampleEnvMap(vec3 reflectDir, float roughness) {
        // Rotate reflection direction by env map rotation
        float cosR = cos(uEnvMapRotation);
        float sinR = sin(uEnvMapRotation);
        vec3 rotatedDir = vec3(
          reflectDir.x * cosR - reflectDir.z * sinR,
          reflectDir.y,
          reflectDir.x * sinR + reflectDir.z * cosR
        );
        
        // Convert direction to equirectangular UV
        float phi = atan(rotatedDir.z, rotatedDir.x);
        float theta = asin(clamp(rotatedDir.y, -1.0, 1.0));
        
        vec2 uv = vec2(
          (phi + PI) / (2.0 * PI),
          (theta + PI * 0.5) / PI
        );
        
        // Sample with mip level based on roughness for blur effect
        float mipLevel = roughness * 6.0;
        vec3 envColor = texture(uEnvMap, uv).rgb;
        
        // Simple blur approximation based on roughness
        if (roughness > 0.1) {
          vec2 offset = vec2(roughness * 0.02);
          envColor += texture(uEnvMap, uv + offset).rgb;
          envColor += texture(uEnvMap, uv - offset).rgb;
          envColor += texture(uEnvMap, uv + vec2(offset.x, -offset.y)).rgb;
          envColor += texture(uEnvMap, uv + vec2(-offset.x, offset.y)).rgb;
          envColor /= 5.0;
        }
        
        return envColor;
      }
      
      // Fresnel-Schlick approximation
      float fresnelSchlick(float cosTheta, float F0) {
        return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
      }
      
      // GGX/Trowbridge-Reitz distribution
      float distributionGGX(vec3 N, vec3 H, float roughness) {
        float a = roughness * roughness;
        float a2 = a * a;
        float NdotH = max(dot(N, H), 0.0);
        float NdotH2 = NdotH * NdotH;
        
        float denom = NdotH2 * (a2 - 1.0) + 1.0;
        denom = PI * denom * denom;
        
        return a2 / max(denom, 0.0001);
      }
      
      // Calculate point light contribution
      vec3 calcPointLight(vec3 lightPos, vec3 lightColor, float lightIntensity, float lightDistance, 
                         vec3 worldPos, vec3 N, vec3 V, vec3 baseColor, float metalness, float roughness) {
        vec3 L = lightPos - worldPos;
        float dist = length(L);
        L = normalize(L);
        
        // Attenuation with distance falloff
        float attenuation = 1.0;
        if (lightDistance > 0.0) {
          attenuation = pow(clamp(1.0 - pow(dist / lightDistance, 4.0), 0.0, 1.0), 2.0) / (dist * dist + 1.0);
        } else {
          attenuation = 1.0 / (dist * dist + 1.0);
        }
        
        vec3 H = normalize(V + L);
        float NdotL = max(dot(N, L), 0.0);
        float NdotH = max(dot(N, H), 0.0);
        
        // Diffuse
        vec3 diffuse = baseColor * NdotL * (1.0 - metalness * 0.8);
        
        // Specular (simplified GGX)
        float D = distributionGGX(N, H, roughness);
        vec3 specular = lightColor * D * NdotL * mix(vec3(1.0), baseColor, metalness);
        
        return (diffuse + specular) * lightColor * lightIntensity * attenuation;
      }
      
      void main() {
        vec3 N = normalize(vNormal);
        vec3 V = normalize(vViewDir);
        
        // Calculate distance from center for gradient
        float distFromCenter = length(vWorldPos.xy) / uTunnelRadius;
        float t = clamp(distFromCenter, 0.0, 1.0);
        
        // Gradient colors based on distance from center
        vec3 baseColor = mix(uColorInner, uColorOuter, t);
        vec3 emissiveColor = mix(uEmissiveInner, uEmissiveOuter, t);
        
        // Add time-varying hue shift
        vec3 hsv = rgb2hsv(baseColor);
        hsv.x += uHueShift * sin(uTime * 0.3 + vPhase * 2.0);
        hsv.y *= uSaturation;
        hsv.z *= uBrightness;
        baseColor = hsv2rgb(hsv);
        
        // Apply same to emissive
        vec3 emHsv = rgb2hsv(emissiveColor);
        emHsv.x += uHueShift * sin(uTime * 0.3 + vPhase * 2.0);
        emHsv.y *= uSaturation;
        emissiveColor = hsv2rgb(emHsv);
        
        // Multiple light sources for interesting reflections
        vec3 light1Dir = normalize(vec3(0.5, 0.8, 1.0));
        vec3 light2Dir = normalize(vec3(-0.3, 0.5, 0.8));
        vec3 light3Dir = normalize(vec3(0.0, 0.0, 1.0));
        
        vec3 light1Color = vec3(1.0, 0.98, 0.95) * 1.5;
        vec3 light2Color = vec3(0.9, 0.92, 1.0) * 1.0;
        vec3 light3Color = vec3(0.95, 0.95, 1.0) * 0.8;
        
        // PBR-like lighting
        vec3 totalDiffuse = vec3(0.0);
        vec3 totalSpecular = vec3(0.0);
        
        // Calculate reflection direction for environment
        vec3 reflectDir = reflect(-V, N);
        
        // Fresnel effect
        float NdotV = max(dot(N, V), 0.0);
        float F0 = mix(0.04, 0.9, uMetalness);
        float fresnel = fresnelSchlick(NdotV, F0);
        fresnel = pow(fresnel, 1.0 / uFresnelPower);
        
        // Process each light
        vec3 lights[3];
        lights[0] = light1Dir;
        lights[1] = light2Dir;
        lights[2] = light3Dir;
        
        vec3 lightColors[3];
        lightColors[0] = light1Color;
        lightColors[1] = light2Color;
        lightColors[2] = light3Color;
        
        for (int i = 0; i < 3; i++) {
          vec3 L = lights[i];
          vec3 H = normalize(V + L);
          
          float NdotL = max(dot(N, L), 0.0);
          float NdotH = max(dot(N, H), 0.0);
          
          // Diffuse (reduced for metals)
          float diffFactor = NdotL * (1.0 - uMetalness * 0.8);
          totalDiffuse += lightColors[i] * diffFactor;
          
          // Specular (GGX)
          float D = distributionGGX(N, H, uRoughness);
          float specFactor = D * NdotL * uSpecularIntensity;
          totalSpecular += lightColors[i] * specFactor;
        }
        
        // Environment map reflection
        vec3 envReflection = sampleEnvMap(reflectDir, uRoughness);
        envReflection *= uEnvMapIntensity;
        
        // Combine lighting
        vec3 diffuse = baseColor * totalDiffuse * 0.4;
        vec3 specular = totalSpecular * mix(vec3(1.0), baseColor, uMetalness) * (1.0 - uRoughness);
        
        // Reflections based on metalness and fresnel
        vec3 reflection = envReflection * fresnel * uReflectivity;
        reflection *= mix(vec3(1.0), baseColor, uMetalness * 0.7);
        
        // Ambient with metallic tint and env contribution
        vec3 ambient = baseColor * 0.15 * (1.0 - uMetalness * 0.5);
        ambient += envReflection * 0.1 * (1.0 - uRoughness); // ambient occlusion from env
        
        // Rim lighting for edge glow
        float rim = pow(1.0 - NdotV, uGlowFalloff) * uGlowStrength;
        vec3 rimColor = emissiveColor * rim;
        
        // Pulse glow
        float pulse = sin(uTime * 2.0 + vPhase * 5.0) * 0.5 + 0.5;
        float glowPulse = pulse * 0.3;
        
        // Calculate dynamic point light contributions
        vec3 pointLight1 = calcPointLight(
          uPointLight1Pos, uPointLight1Color, uPointLight1Intensity, uPointLight1Distance,
          vWorldPos, N, V, baseColor, uMetalness, uRoughness
        );
        vec3 pointLight2 = calcPointLight(
          uPointLight2Pos, uPointLight2Color, uPointLight2Intensity, uPointLight2Distance,
          vWorldPos, N, V, baseColor, uMetalness, uRoughness
        );
        
        // Combine all lighting components
        vec3 finalColor = ambient + diffuse + specular + reflection;
        
        // Add dynamic point lights
        finalColor += pointLight1;
        finalColor += pointLight2;
        
        // Add emissive and glow
        finalColor += emissiveColor * (uEmissiveIntensity * 0.5 + glowPulse);
        finalColor += rimColor;
        
        // Distance-based glow (brighter near center)
        float centerGlow = (1.0 - t) * 0.3 * uGlowStrength;
        finalColor += emissiveColor * centerGlow;
        
        // Metallic sheen effect - adds sparkle with env map influence
        float sparkle = pow(max(dot(reflectDir, light1Dir), 0.0), uSpecularPower) * (1.0 - uRoughness);
        finalColor += envReflection * sparkle * uMetalness * 0.5;
        finalColor += vec3(1.0, 1.0, 1.0) * sparkle * uMetalness * 0.5;
        
        // Apply fog
        float fogFactor = smoothstep(uFogNear, uFogFar, vDepth);
        finalColor = mix(finalColor, uFogColor, fogFactor);
        
        gl_FragColor = vec4(finalColor, 1.0);
      }
    `,
    side: THREE.DoubleSide,
  });
  
  // Create instanced mesh
  const instancedMesh = new THREE.InstancedMesh(voxelGeo, voxelMaterial, count);
  
  // Instance attributes
  const offsets = new Float32Array(count * 3);
  const scales = new Float32Array(count);
  const phases = new Float32Array(count);
  const ringTs = new Float32Array(count);
  const normals = new Float32Array(count * 3);
  const rotAxes = new Float32Array(count * 3);
  const rotSpeeds = new Float32Array(count);
  
  // Place voxels in a tunnel formation
  let placed = 0;
  const rings = config.rings || Math.floor(tunnelLength / (voxelSize + voxelGap));
  const voxelsPerRing = config.voxelsPerRing || Math.floor(2 * Math.PI * tunnelRadius / (voxelSize + voxelGap));
  
  for (let ring = 0; ring < rings && placed < count; ring++) {
    const z = (ring / rings - 0.5) * tunnelLength + tunnelOffsetZ;
    const ringT = ring / rings;
    
    for (let v = 0; v < voxelsPerRing && placed < count; v++) {
      // Random density check
      if (Math.random() > density) continue;
      
      const theta = (v / voxelsPerRing) * Math.PI * 2 + (ring % 2) * (Math.PI / voxelsPerRing);
      
      // Position on cylinder surface
      const x = Math.cos(theta) * tunnelRadius;
      const y = Math.sin(theta) * tunnelRadius;
      
      // Add some randomness
      const jitterR = (Math.random() - 0.5) * voxelSize * 0.5;
      const jitterZ = (Math.random() - 0.5) * voxelSize * 0.5;
      
      offsets[placed * 3] = x + Math.cos(theta) * jitterR;
      offsets[placed * 3 + 1] = y + Math.sin(theta) * jitterR;
      offsets[placed * 3 + 2] = z + jitterZ;
      
      // Normal points toward center
      normals[placed * 3] = -Math.cos(theta);
      normals[placed * 3 + 1] = -Math.sin(theta);
      normals[placed * 3 + 2] = 0;
      
      // Scale with variation
      scales[placed] = 1.0 + (Math.random() - 0.5) * sizeVariation * 2;
      
      // Phase for animation variety
      phases[placed] = Math.random() * Math.PI * 2;
      
      // Ring position (0-1 along tunnel)
      ringTs[placed] = ringT;
      
      // Random rotation axis (normalized)
      const rx = Math.random() - 0.5;
      const ry = Math.random() - 0.5;
      const rz = Math.random() - 0.5;
      const rLen = Math.sqrt(rx*rx + ry*ry + rz*rz) || 1;
      rotAxes[placed * 3] = rx / rLen;
      rotAxes[placed * 3 + 1] = ry / rLen;
      rotAxes[placed * 3 + 2] = rz / rLen;
      
      // Random rotation speed (varied for visual interest)
      rotSpeeds[placed] = 0.5 + Math.random() * 1.5;
      
      placed++;
    }
  }
  
  console.log(`Voxel tunnel: placed ${placed}/${count} voxels`);
  
  // Add instance attributes
  instancedMesh.geometry.setAttribute('instanceOffset', new THREE.InstancedBufferAttribute(offsets, 3));
  instancedMesh.geometry.setAttribute('instanceScale', new THREE.InstancedBufferAttribute(scales, 1));
  instancedMesh.geometry.setAttribute('instancePhase', new THREE.InstancedBufferAttribute(phases, 1));
  instancedMesh.geometry.setAttribute('instanceRingT', new THREE.InstancedBufferAttribute(ringTs, 1));
  instancedMesh.geometry.setAttribute('instanceNormal', new THREE.InstancedBufferAttribute(normals, 3));
  instancedMesh.geometry.setAttribute('instanceRotAxis', new THREE.InstancedBufferAttribute(rotAxes, 3));
  instancedMesh.geometry.setAttribute('instanceRotSpeed', new THREE.InstancedBufferAttribute(rotSpeeds, 1));
  
  // Dummy matrices
  const dummy = new THREE.Matrix4();
  for (let i = 0; i < count; i++) {
    dummy.identity();
    instancedMesh.setMatrixAt(i, dummy);
  }
  instancedMesh.instanceMatrix.needsUpdate = true;
  
  // Disable frustum culling
  instancedMesh.frustumCulled = false;
  
  scene.add(instancedMesh);
  
  return instancedMesh;
}
