{
	"$schema": "https://ui.shadcn.com/schema/registry-item.json",
	"name": "Hyperspeed-JS-CSS",
	"title": "Hyperspeed",
	"description": "Animated lines continuously moving to simulate hyperspace travel on click hold.",
	"type": "registry:component",
	"files": [
		{
			"type": "registry:component",
			"path": "HyperSpeedPresets.js",
			"content": "export const hyperspeedPresets = {\n  one: {\n    onSpeedUp: () => {},\n    onSlowDown: () => {},\n    distortion: 'turbulentDistortion',\n    length: 400,\n    roadWidth: 10,\n    islandWidth: 2,\n    lanesPerRoad: 3,\n    fov: 90,\n    fovSpeedUp: 150,\n    speedUp: 2,\n    carLightsFade: 0.4,\n    totalSideLightSticks: 20,\n    lightPairsPerRoadWay: 40,\n    shoulderLinesWidthPercentage: 0.05,\n    brokenLinesWidthPercentage: 0.1,\n    brokenLinesLengthPercentage: 0.5,\n    lightStickWidth: [0.12, 0.5],\n    lightStickHeight: [1.3, 1.7],\n    movingAwaySpeed: [60, 80],\n    movingCloserSpeed: [-120, -160],\n    carLightsLength: [400 * 0.03, 400 * 0.2],\n    carLightsRadius: [0.05, 0.14],\n    carWidthPercentage: [0.3, 0.5],\n    carShiftX: [-0.8, 0.8],\n    carFloorSeparation: [0, 5],\n    colors: {\n      roadColor: 0x080808,\n      islandColor: 0x0a0a0a,\n      background: 0x000000,\n      shoulderLines: 0x131318,\n      brokenLines: 0x131318,\n      leftCars: [0xd856bf, 0x6750a2, 0xc247ac],\n      rightCars: [0x03b3c3, 0x0e5ea5, 0x324555],\n      sticks: 0x03b3c3\n    }\n  },\n  two: {\n    onSpeedUp: () => {},\n    onSlowDown: () => {},\n    distortion: 'mountainDistortion',\n    length: 400,\n    roadWidth: 9,\n    islandWidth: 2,\n    lanesPerRoad: 3,\n    fov: 90,\n    fovSpeedUp: 150,\n    speedUp: 2,\n    carLightsFade: 0.4,\n    totalSideLightSticks: 50,\n    lightPairsPerRoadWay: 50,\n    shoulderLinesWidthPercentage: 0.05,\n    brokenLinesWidthPercentage: 0.1,\n    brokenLinesLengthPercentage: 0.5,\n    lightStickWidth: [0.12, 0.5],\n    lightStickHeight: [1.3, 1.7],\n\n    movingAwaySpeed: [60, 80],\n    movingCloserSpeed: [-120, -160],\n    carLightsLength: [400 * 0.05, 400 * 0.15],\n    carLightsRadius: [0.05, 0.14],\n    carWidthPercentage: [0.3, 0.5],\n    carShiftX: [-0.2, 0.2],\n    carFloorSeparation: [0.05, 1],\n    colors: {\n      roadColor: 0x080808,\n      islandColor: 0x0a0a0a,\n      background: 0x000000,\n      shoulderLines: 0x131318,\n      brokenLines: 0x131318,\n      leftCars: [0xff102a, 0xeb383e, 0xff102a],\n      rightCars: [0xdadafa, 0xbebae3, 0x8f97e4],\n      sticks: 0xdadafa\n    }\n  },\n  three: {\n    onSpeedUp: () => {},\n    onSlowDown: () => {},\n    distortion: 'xyDistortion',\n    length: 400,\n    roadWidth: 9,\n    islandWidth: 2,\n    lanesPerRoad: 3,\n    fov: 90,\n    fovSpeedUp: 150,\n    speedUp: 3,\n    carLightsFade: 0.4,\n    totalSideLightSticks: 50,\n    lightPairsPerRoadWay: 30,\n    shoulderLinesWidthPercentage: 0.05,\n    brokenLinesWidthPercentage: 0.1,\n    brokenLinesLengthPercentage: 0.5,\n    lightStickWidth: [0.02, 0.05],\n    lightStickHeight: [0.3, 0.7],\n    movingAwaySpeed: [20, 50],\n    movingCloserSpeed: [-150, -230],\n    carLightsLength: [400 * 0.05, 400 * 0.2],\n    carLightsRadius: [0.03, 0.08],\n    carWidthPercentage: [0.1, 0.5],\n    carShiftX: [-0.5, 0.5],\n    carFloorSeparation: [0, 0.1],\n    colors: {\n      roadColor: 0x080808,\n      islandColor: 0x0a0a0a,\n      background: 0x000000,\n      shoulderLines: 0x131318,\n      brokenLines: 0x131318,\n      leftCars: [0x7d0d1b, 0xa90519, 0xff102a],\n      rightCars: [0xf1eece, 0xe6e2b1, 0xdfd98a],\n      sticks: 0xf1eece\n    }\n  },\n  four: {\n    onSpeedUp: () => {},\n    onSlowDown: () => {},\n    distortion: 'LongRaceDistortion',\n    length: 400,\n    roadWidth: 10,\n    islandWidth: 5,\n    lanesPerRoad: 2,\n    fov: 90,\n    fovSpeedUp: 150,\n    speedUp: 2,\n    carLightsFade: 0.4,\n    totalSideLightSticks: 50,\n    lightPairsPerRoadWay: 70,\n    shoulderLinesWidthPercentage: 0.05,\n    brokenLinesWidthPercentage: 0.1,\n    brokenLinesLengthPercentage: 0.5,\n    lightStickWidth: [0.12, 0.5],\n    lightStickHeight: [1.3, 1.7],\n    movingAwaySpeed: [60, 80],\n    movingCloserSpeed: [-120, -160],\n    carLightsLength: [400 * 0.05, 400 * 0.15],\n    carLightsRadius: [0.05, 0.14],\n    carWidthPercentage: [0.3, 0.5],\n    carShiftX: [-0.2, 0.2],\n    carFloorSeparation: [0.05, 1],\n    colors: {\n      roadColor: 0x080808,\n      islandColor: 0x0a0a0a,\n      background: 0x000000,\n      shoulderLines: 0x131318,\n      brokenLines: 0x131318,\n      leftCars: [0xff5f73, 0xe74d60, 0xff102a],\n      rightCars: [0xa4e3e6, 0x80d1d4, 0x53c2c6],\n      sticks: 0xa4e3e6\n    }\n  },\n  five: {\n    onSpeedUp: () => {},\n    onSlowDown: () => {},\n    distortion: 'turbulentDistortion',\n    length: 400,\n    roadWidth: 9,\n    islandWidth: 2,\n    lanesPerRoad: 3,\n    fov: 90,\n    fovSpeedUp: 150,\n    speedUp: 2,\n    carLightsFade: 0.4,\n    totalSideLightSticks: 50,\n    lightPairsPerRoadWay: 50,\n    shoulderLinesWidthPercentage: 0.05,\n    brokenLinesWidthPercentage: 0.1,\n    brokenLinesLengthPercentage: 0.5,\n    lightStickWidth: [0.12, 0.5],\n    lightStickHeight: [1.3, 1.7],\n    movingAwaySpeed: [60, 80],\n    movingCloserSpeed: [-120, -160],\n    carLightsLength: [400 * 0.05, 400 * 0.15],\n    carLightsRadius: [0.05, 0.14],\n    carWidthPercentage: [0.3, 0.5],\n    carShiftX: [-0.2, 0.2],\n    carFloorSeparation: [0.05, 1],\n    colors: {\n      roadColor: 0x080808,\n      islandColor: 0x0a0a0a,\n      background: 0x000000,\n      shoulderLines: 0x131318,\n      brokenLines: 0x131318,\n      leftCars: [0xdc5b20, 0xdca320, 0xdc2020],\n      rightCars: [0x334bf7, 0xe5e6ed, 0xbfc6f3],\n      sticks: 0xc5e8eb\n    }\n  },\n  six: {\n    onSpeedUp: () => {},\n    onSlowDown: () => {},\n    distortion: 'deepDistortion',\n    length: 400,\n    roadWidth: 18,\n    islandWidth: 2,\n    lanesPerRoad: 3,\n    fov: 90,\n    fovSpeedUp: 150,\n    speedUp: 2,\n    carLightsFade: 0.4,\n    totalSideLightSticks: 50,\n    lightPairsPerRoadWay: 50,\n    shoulderLinesWidthPercentage: 0.05,\n    brokenLinesWidthPercentage: 0.1,\n    brokenLinesLengthPercentage: 0.5,\n    lightStickWidth: [0.12, 0.5],\n    lightStickHeight: [1.3, 1.7],\n    movingAwaySpeed: [60, 80],\n    movingCloserSpeed: [-120, -160],\n    carLightsLength: [400 * 0.05, 400 * 0.15],\n    carLightsRadius: [0.05, 0.14],\n    carWidthPercentage: [0.3, 0.5],\n    carShiftX: [-0.2, 0.2],\n    carFloorSeparation: [0.05, 1],\n    colors: {\n      roadColor: 0x080808,\n      islandColor: 0x0a0a0a,\n      background: 0x000000,\n      shoulderLines: 0x131318,\n      brokenLines: 0x131318,\n      leftCars: [0xff322f, 0xa33010, 0xa81508],\n      rightCars: [0xfdfdf0, 0xf3dea0, 0xe2bb88],\n      sticks: 0xfdfdf0\n    }\n  }\n};\n"
		},
		{
			"type": "registry:file",
			"path": "Hyperspeed.css",
			"target": "@components/Hyperspeed.css",
			"content": "#lights {\n  width: 100%;\n  height: 100%;\n  overflow: hidden;\n  position: absolute;\n}\n\ncanvas {\n  width: 100%;\n  height: 100%;\n}\n"
		},
		{
			"type": "registry:component",
			"path": "Hyperspeed.jsx",
			"content": "import { BloomEffect, EffectComposer, EffectPass, RenderPass, SMAAEffect, SMAAPreset } from 'postprocessing';\nimport { useEffect, useRef } from 'react';\nimport * as THREE from 'three';\n\nimport './Hyperspeed.css';\n\nconst DEFAULT_EFFECT_OPTIONS = {\n  onSpeedUp: () => {},\n  onSlowDown: () => {},\n  distortion: 'turbulentDistortion',\n  length: 400,\n  roadWidth: 10,\n  islandWidth: 2,\n  lanesPerRoad: 4,\n  fov: 90,\n  fovSpeedUp: 150,\n  speedUp: 2,\n  carLightsFade: 0.4,\n  totalSideLightSticks: 20,\n  lightPairsPerRoadWay: 40,\n  shoulderLinesWidthPercentage: 0.05,\n  brokenLinesWidthPercentage: 0.1,\n  brokenLinesLengthPercentage: 0.5,\n  lightStickWidth: [0.12, 0.5],\n  lightStickHeight: [1.3, 1.7],\n  movingAwaySpeed: [60, 80],\n  movingCloserSpeed: [-120, -160],\n  carLightsLength: [400 * 0.03, 400 * 0.2],\n  carLightsRadius: [0.05, 0.14],\n  carWidthPercentage: [0.3, 0.5],\n  carShiftX: [-0.8, 0.8],\n  carFloorSeparation: [0, 5],\n  colors: {\n    roadColor: 0x080808,\n    islandColor: 0x0a0a0a,\n    background: 0x000000,\n    shoulderLines: 0xffffff,\n    brokenLines: 0xffffff,\n    leftCars: [0xd856bf, 0x6750a2, 0xc247ac],\n    rightCars: [0x03b3c3, 0x0e5ea5, 0x324555],\n    sticks: 0x03b3c3\n  }\n};\n\nconst Hyperspeed = ({ effectOptions = DEFAULT_EFFECT_OPTIONS, lightMode = false }) => {\n  const hyperspeed = useRef(null);\n  const appRef = useRef(null);\n\n  useEffect(() => {\n    if (appRef.current) {\n      appRef.current.dispose();\n      appRef.current = null;\n      const container = hyperspeed.current;\n      if (container) {\n        while (container.firstChild) {\n          container.removeChild(container.firstChild);\n        }\n      }\n    }\n    const mountainUniforms = {\n      uFreq: { value: new THREE.Vector3(3, 6, 10) },\n      uAmp: { value: new THREE.Vector3(30, 30, 20) }\n    };\n\n    const xyUniforms = {\n      uFreq: { value: new THREE.Vector2(5, 2) },\n      uAmp: { value: new THREE.Vector2(25, 15) }\n    };\n\n    const LongRaceUniforms = {\n      uFreq: { value: new THREE.Vector2(2, 3) },\n      uAmp: { value: new THREE.Vector2(35, 10) }\n    };\n\n    const turbulentUniforms = {\n      uFreq: { value: new THREE.Vector4(4, 8, 8, 1) },\n      uAmp: { value: new THREE.Vector4(25, 5, 10, 10) }\n    };\n\n    const deepUniforms = {\n      uFreq: { value: new THREE.Vector2(4, 8) },\n      uAmp: { value: new THREE.Vector2(10, 20) },\n      uPowY: { value: new THREE.Vector2(20, 2) }\n    };\n\n    let nsin = val => Math.sin(val) * 0.5 + 0.5;\n\n    const distortions = {\n      mountainDistortion: {\n        uniforms: mountainUniforms,\n        getDistortion: `\n          uniform vec3 uAmp;\n          uniform vec3 uFreq;\n          #define PI 3.14159265358979\n          float nsin(float val){\n            return sin(val) * 0.5 + 0.5;\n          }\n          vec3 getDistortion(float progress){\n            float movementProgressFix = 0.02;\n            return vec3( \n              cos(progress * PI * uFreq.x + uTime) * uAmp.x - cos(movementProgressFix * PI * uFreq.x + uTime) * uAmp.x,\n              nsin(progress * PI * uFreq.y + uTime) * uAmp.y - nsin(movementProgressFix * PI * uFreq.y + uTime) * uAmp.y,\n              nsin(progress * PI * uFreq.z + uTime) * uAmp.z - nsin(movementProgressFix * PI * uFreq.z + uTime) * uAmp.z\n            );\n          }\n        `,\n        getJS: (progress, time) => {\n          let movementProgressFix = 0.02;\n          let uFreq = mountainUniforms.uFreq.value;\n          let uAmp = mountainUniforms.uAmp.value;\n          let distortion = new THREE.Vector3(\n            Math.cos(progress * Math.PI * uFreq.x + time) * uAmp.x -\n              Math.cos(movementProgressFix * Math.PI * uFreq.x + time) * uAmp.x,\n            nsin(progress * Math.PI * uFreq.y + time) * uAmp.y -\n              nsin(movementProgressFix * Math.PI * uFreq.y + time) * uAmp.y,\n            nsin(progress * Math.PI * uFreq.z + time) * uAmp.z -\n              nsin(movementProgressFix * Math.PI * uFreq.z + time) * uAmp.z\n          );\n          let lookAtAmp = new THREE.Vector3(2, 2, 2);\n          let lookAtOffset = new THREE.Vector3(0, 0, -5);\n          return distortion.multiply(lookAtAmp).add(lookAtOffset);\n        }\n      },\n      xyDistortion: {\n        uniforms: xyUniforms,\n        getDistortion: `\n          uniform vec2 uFreq;\n          uniform vec2 uAmp;\n          #define PI 3.14159265358979\n          vec3 getDistortion(float progress){\n            float movementProgressFix = 0.02;\n            return vec3( \n              cos(progress * PI * uFreq.x + uTime) * uAmp.x - cos(movementProgressFix * PI * uFreq.x + uTime) * uAmp.x,\n              sin(progress * PI * uFreq.y + PI/2. + uTime) * uAmp.y - sin(movementProgressFix * PI * uFreq.y + PI/2. + uTime) * uAmp.y,\n              0.\n            );\n          }\n        `,\n        getJS: (progress, time) => {\n          let movementProgressFix = 0.02;\n          let uFreq = xyUniforms.uFreq.value;\n          let uAmp = xyUniforms.uAmp.value;\n          let distortion = new THREE.Vector3(\n            Math.cos(progress * Math.PI * uFreq.x + time) * uAmp.x -\n              Math.cos(movementProgressFix * Math.PI * uFreq.x + time) * uAmp.x,\n            Math.sin(progress * Math.PI * uFreq.y + time + Math.PI / 2) * uAmp.y -\n              Math.sin(movementProgressFix * Math.PI * uFreq.y + time + Math.PI / 2) * uAmp.y,\n            0\n          );\n          let lookAtAmp = new THREE.Vector3(2, 0.4, 1);\n          let lookAtOffset = new THREE.Vector3(0, 0, -3);\n          return distortion.multiply(lookAtAmp).add(lookAtOffset);\n        }\n      },\n      LongRaceDistortion: {\n        uniforms: LongRaceUniforms,\n        getDistortion: `\n          uniform vec2 uFreq;\n          uniform vec2 uAmp;\n          #define PI 3.14159265358979\n          vec3 getDistortion(float progress){\n            float camProgress = 0.0125;\n            return vec3( \n              sin(progress * PI * uFreq.x + uTime) * uAmp.x - sin(camProgress * PI * uFreq.x + uTime) * uAmp.x,\n              sin(progress * PI * uFreq.y + uTime) * uAmp.y - sin(camProgress * PI * uFreq.y + uTime) * uAmp.y,\n              0.\n            );\n          }\n        `,\n        getJS: (progress, time) => {\n          let camProgress = 0.0125;\n          let uFreq = LongRaceUniforms.uFreq.value;\n          let uAmp = LongRaceUniforms.uAmp.value;\n          let distortion = new THREE.Vector3(\n            Math.sin(progress * Math.PI * uFreq.x + time) * uAmp.x -\n              Math.sin(camProgress * Math.PI * uFreq.x + time) * uAmp.x,\n            Math.sin(progress * Math.PI * uFreq.y + time) * uAmp.y -\n              Math.sin(camProgress * Math.PI * uFreq.y + time) * uAmp.y,\n            0\n          );\n          let lookAtAmp = new THREE.Vector3(1, 1, 0);\n          let lookAtOffset = new THREE.Vector3(0, 0, -5);\n          return distortion.multiply(lookAtAmp).add(lookAtOffset);\n        }\n      },\n      turbulentDistortion: {\n        uniforms: turbulentUniforms,\n        getDistortion: `\n          uniform vec4 uFreq;\n          uniform vec4 uAmp;\n          float nsin(float val){\n            return sin(val) * 0.5 + 0.5;\n          }\n          #define PI 3.14159265358979\n          float getDistortionX(float progress){\n            return (\n              cos(PI * progress * uFreq.r + uTime) * uAmp.r +\n              pow(cos(PI * progress * uFreq.g + uTime * (uFreq.g / uFreq.r)), 2. ) * uAmp.g\n            );\n          }\n          float getDistortionY(float progress){\n            return (\n              -nsin(PI * progress * uFreq.b + uTime) * uAmp.b +\n              -pow(nsin(PI * progress * uFreq.a + uTime / (uFreq.b / uFreq.a)), 5.) * uAmp.a\n            );\n          }\n          vec3 getDistortion(float progress){\n            return vec3(\n              getDistortionX(progress) - getDistortionX(0.0125),\n              getDistortionY(progress) - getDistortionY(0.0125),\n              0.\n            );\n          }\n        `,\n        getJS: (progress, time) => {\n          const uFreq = turbulentUniforms.uFreq.value;\n          const uAmp = turbulentUniforms.uAmp.value;\n\n          const getX = p =>\n            Math.cos(Math.PI * p * uFreq.x + time) * uAmp.x +\n            Math.pow(Math.cos(Math.PI * p * uFreq.y + time * (uFreq.y / uFreq.x)), 2) * uAmp.y;\n\n          const getY = p =>\n            -nsin(Math.PI * p * uFreq.z + time) * uAmp.z -\n            Math.pow(nsin(Math.PI * p * uFreq.w + time / (uFreq.z / uFreq.w)), 5) * uAmp.w;\n\n          let distortion = new THREE.Vector3(\n            getX(progress) - getX(progress + 0.007),\n            getY(progress) - getY(progress + 0.007),\n            0\n          );\n          let lookAtAmp = new THREE.Vector3(-2, -5, 0);\n          let lookAtOffset = new THREE.Vector3(0, 0, -10);\n          return distortion.multiply(lookAtAmp).add(lookAtOffset);\n        }\n      },\n      turbulentDistortionStill: {\n        uniforms: turbulentUniforms,\n        getDistortion: `\n          uniform vec4 uFreq;\n          uniform vec4 uAmp;\n          float nsin(float val){\n            return sin(val) * 0.5 + 0.5;\n          }\n          #define PI 3.14159265358979\n          float getDistortionX(float progress){\n            return (\n              cos(PI * progress * uFreq.r) * uAmp.r +\n              pow(cos(PI * progress * uFreq.g * (uFreq.g / uFreq.r)), 2. ) * uAmp.g\n            );\n          }\n          float getDistortionY(float progress){\n            return (\n              -nsin(PI * progress * uFreq.b) * uAmp.b +\n              -pow(nsin(PI * progress * uFreq.a / (uFreq.b / uFreq.a)), 5.) * uAmp.a\n            );\n          }\n          vec3 getDistortion(float progress){\n            return vec3(\n              getDistortionX(progress) - getDistortionX(0.02),\n              getDistortionY(progress) - getDistortionY(0.02),\n              0.\n            );\n          }\n        `\n      },\n      deepDistortionStill: {\n        uniforms: deepUniforms,\n        getDistortion: `\n          uniform vec4 uFreq;\n          uniform vec4 uAmp;\n          uniform vec2 uPowY;\n          float nsin(float val){\n            return sin(val) * 0.5 + 0.5;\n          }\n          #define PI 3.14159265358979\n          float getDistortionX(float progress){\n            return (\n              sin(progress * PI * uFreq.x) * uAmp.x * 2.\n            );\n          }\n          float getDistortionY(float progress){\n            return (\n              pow(abs(progress * uPowY.x), uPowY.y) + sin(progress * PI * uFreq.y) * uAmp.y\n            );\n          }\n          vec3 getDistortion(float progress){\n            return vec3(\n              getDistortionX(progress) - getDistortionX(0.02),\n              getDistortionY(progress) - getDistortionY(0.05),\n              0.\n            );\n          }\n        `\n      },\n      deepDistortion: {\n        uniforms: deepUniforms,\n        getDistortion: `\n          uniform vec4 uFreq;\n          uniform vec4 uAmp;\n          uniform vec2 uPowY;\n          float nsin(float val){\n            return sin(val) * 0.5 + 0.5;\n          }\n          #define PI 3.14159265358979\n          float getDistortionX(float progress){\n            return (\n              sin(progress * PI * uFreq.x + uTime) * uAmp.x\n            );\n          }\n          float getDistortionY(float progress){\n            return (\n              pow(abs(progress * uPowY.x), uPowY.y) + sin(progress * PI * uFreq.y + uTime) * uAmp.y\n            );\n          }\n          vec3 getDistortion(float progress){\n            return vec3(\n              getDistortionX(progress) - getDistortionX(0.02),\n              getDistortionY(progress) - getDistortionY(0.02),\n              0.\n            );\n          }\n        `,\n        getJS: (progress, time) => {\n          const uFreq = deepUniforms.uFreq.value;\n          const uAmp = deepUniforms.uAmp.value;\n          const uPowY = deepUniforms.uPowY.value;\n\n          const getX = p => Math.sin(p * Math.PI * uFreq.x + time) * uAmp.x;\n          const getY = p => Math.pow(p * uPowY.x, uPowY.y) + Math.sin(p * Math.PI * uFreq.y + time) * uAmp.y;\n\n          let distortion = new THREE.Vector3(\n            getX(progress) - getX(progress + 0.01),\n            getY(progress) - getY(progress + 0.01),\n            0\n          );\n          let lookAtAmp = new THREE.Vector3(-2, -4, 0);\n          let lookAtOffset = new THREE.Vector3(0, 0, -10);\n          return distortion.multiply(lookAtAmp).add(lookAtOffset);\n        }\n      }\n    };\n\n    class App {\n      constructor(container, options = {}) {\n        this.options = options;\n        if (this.options.distortion == null) {\n          this.options.distortion = {\n            uniforms: distortion_uniforms,\n            getDistortion: distortion_vertex\n          };\n        }\n        this.container = container;\n        this.hasValidSize = false;\n\n        const initW = Math.max(1, container.offsetWidth);\n        const initH = Math.max(1, container.offsetHeight);\n\n        this.renderer = new THREE.WebGLRenderer({\n          antialias: false,\n          alpha: true\n        });\n        this.renderer.setSize(initW, initH, false);\n        this.renderer.setPixelRatio(window.devicePixelRatio);\n        this.composer = new EffectComposer(this.renderer);\n        container.append(this.renderer.domElement);\n\n        this.camera = new THREE.PerspectiveCamera(options.fov, initW / initH, 0.1, 10000);\n        this.camera.position.z = -5;\n        this.camera.position.y = 8;\n        this.camera.position.x = 0;\n        this.scene = new THREE.Scene();\n        this.scene.background = null;\n\n        let fog = new THREE.Fog(options.colors.background, options.length * 0.2, options.length * 500);\n        this.scene.fog = fog;\n        this.fogUniforms = {\n          fogColor: { value: fog.color },\n          fogNear: { value: fog.near },\n          fogFar: { value: fog.far }\n        };\n        this.timer = new THREE.Timer();\n        this.timer.connect(document);\n        this.assets = {};\n        this.disposed = false;\n\n        this.road = new Road(this, options);\n        this.leftCarLights = new CarLights(\n          this,\n          options,\n          options.colors.leftCars,\n          options.movingAwaySpeed,\n          new THREE.Vector2(0, 1 - options.carLightsFade)\n        );\n        this.rightCarLights = new CarLights(\n          this,\n          options,\n          options.colors.rightCars,\n          options.movingCloserSpeed,\n          new THREE.Vector2(1, 0 + options.carLightsFade)\n        );\n        this.leftSticks = new LightsSticks(this, options);\n\n        this.fovTarget = options.fov;\n        this.speedUpTarget = 0;\n        this.speedUp = 0;\n        this.timeOffset = 0;\n\n        this.tick = this.tick.bind(this);\n        this.init = this.init.bind(this);\n        this.setSize = this.setSize.bind(this);\n        this.onMouseDown = this.onMouseDown.bind(this);\n        this.onMouseUp = this.onMouseUp.bind(this);\n\n        this.onTouchStart = this.onTouchStart.bind(this);\n        this.onTouchEnd = this.onTouchEnd.bind(this);\n        this.onContextMenu = this.onContextMenu.bind(this);\n\n        this.onWindowResize = this.onWindowResize.bind(this);\n        window.addEventListener('resize', this.onWindowResize);\n\n        if (container.offsetWidth > 0 && container.offsetHeight > 0) {\n          this.hasValidSize = true;\n        }\n      }\n\n      onWindowResize() {\n        const width = this.container.offsetWidth;\n        const height = this.container.offsetHeight;\n\n        if (width <= 0 || height <= 0) {\n          this.hasValidSize = false;\n          return;\n        }\n\n        this.renderer.setSize(width, height);\n        this.camera.aspect = width / height;\n        this.camera.updateProjectionMatrix();\n        this.composer.setSize(width, height);\n        this.hasValidSize = true;\n      }\n\n      initPasses() {\n        this.renderPass = new RenderPass(this.scene, this.camera);\n        this.bloomPass = new EffectPass(\n          this.camera,\n          new BloomEffect({\n            luminanceThreshold: 0.2,\n            luminanceSmoothing: 0,\n            resolutionScale: 1\n          })\n        );\n\n        const smaaPass = new EffectPass(\n          this.camera,\n          new SMAAEffect({\n            preset: SMAAPreset.MEDIUM,\n            searchImage: SMAAEffect.searchImageDataURL,\n            areaImage: SMAAEffect.areaImageDataURL\n          })\n        );\n        this.renderPass.renderToScreen = false;\n        this.bloomPass.renderToScreen = false;\n        smaaPass.renderToScreen = true;\n        this.composer.addPass(this.renderPass);\n        this.composer.addPass(this.bloomPass);\n        this.composer.addPass(smaaPass);\n      }\n\n      loadAssets() {\n        const assets = this.assets;\n        return new Promise(resolve => {\n          const manager = new THREE.LoadingManager(resolve);\n\n          const searchImage = new Image();\n          const areaImage = new Image();\n          assets.smaa = {};\n          searchImage.addEventListener('load', function () {\n            assets.smaa.search = this;\n            manager.itemEnd('smaa-search');\n          });\n\n          areaImage.addEventListener('load', function () {\n            assets.smaa.area = this;\n            manager.itemEnd('smaa-area');\n          });\n          manager.itemStart('smaa-search');\n          manager.itemStart('smaa-area');\n\n          searchImage.src = SMAAEffect.searchImageDataURL;\n          areaImage.src = SMAAEffect.areaImageDataURL;\n        });\n      }\n\n      init() {\n        this.initPasses();\n        const options = this.options;\n        this.road.init();\n        this.leftCarLights.init();\n\n        this.leftCarLights.mesh.position.setX(-options.roadWidth / 2 - options.islandWidth / 2);\n        this.rightCarLights.init();\n        this.rightCarLights.mesh.position.setX(options.roadWidth / 2 + options.islandWidth / 2);\n        this.leftSticks.init();\n        this.leftSticks.mesh.position.setX(-(options.roadWidth + options.islandWidth / 2));\n\n        this.container.addEventListener('mousedown', this.onMouseDown);\n        this.container.addEventListener('mouseup', this.onMouseUp);\n        this.container.addEventListener('mouseout', this.onMouseUp);\n\n        this.container.addEventListener('touchstart', this.onTouchStart, { passive: true });\n        this.container.addEventListener('touchend', this.onTouchEnd, { passive: true });\n        this.container.addEventListener('touchcancel', this.onTouchEnd, { passive: true });\n\n        this.container.addEventListener('contextmenu', this.onContextMenu);\n\n        this.tick();\n      }\n\n      onMouseDown(ev) {\n        if (this.options.onSpeedUp) this.options.onSpeedUp(ev);\n        this.fovTarget = this.options.fovSpeedUp;\n        this.speedUpTarget = this.options.speedUp;\n      }\n\n      onMouseUp(ev) {\n        if (this.options.onSlowDown) this.options.onSlowDown(ev);\n        this.fovTarget = this.options.fov;\n        this.speedUpTarget = 0;\n      }\n\n      onTouchStart(ev) {\n        if (this.options.onSpeedUp) this.options.onSpeedUp(ev);\n        this.fovTarget = this.options.fovSpeedUp;\n        this.speedUpTarget = this.options.speedUp;\n      }\n\n      onTouchEnd(ev) {\n        if (this.options.onSlowDown) this.options.onSlowDown(ev);\n        this.fovTarget = this.options.fov;\n        this.speedUpTarget = 0;\n      }\n\n      onContextMenu(ev) {\n        ev.preventDefault();\n      }\n\n      update(delta) {\n        let lerpPercentage = Math.exp(-(-60 * Math.log2(1 - 0.1)) * delta);\n        this.speedUp += lerp(this.speedUp, this.speedUpTarget, lerpPercentage, 0.00001);\n        this.timeOffset += this.speedUp * delta;\n\n        const time = this.timer.getElapsed() + this.timeOffset;\n\n        this.rightCarLights.update(time);\n        this.leftCarLights.update(time);\n        this.leftSticks.update(time);\n        this.road.update(time);\n\n        let updateCamera = false;\n        let fovChange = lerp(this.camera.fov, this.fovTarget, lerpPercentage);\n        if (fovChange !== 0) {\n          this.camera.fov += fovChange * delta * 6;\n          updateCamera = true;\n        }\n\n        if (this.options.distortion.getJS) {\n          const distortion = this.options.distortion.getJS(0.025, time);\n\n          this.camera.lookAt(\n            new THREE.Vector3(\n              this.camera.position.x + distortion.x,\n              this.camera.position.y + distortion.y,\n              this.camera.position.z + distortion.z\n            )\n          );\n          updateCamera = true;\n        }\n        if (updateCamera) {\n          this.camera.updateProjectionMatrix();\n        }\n      }\n\n      render(delta) {\n        this.composer.render(delta);\n      }\n\n      dispose() {\n        this.disposed = true;\n        this.timer.dispose();\n\n        if (this.scene) {\n          this.scene.traverse(object => {\n            const obj = object;\n            if (!obj.isMesh) return;\n\n            if (obj.geometry) obj.geometry.dispose();\n\n            if (obj.material) {\n              if (Array.isArray(obj.material)) {\n                obj.material.forEach(material => material.dispose());\n              } else {\n                obj.material.dispose();\n              }\n            }\n          });\n          this.scene.clear();\n        }\n\n        if (this.renderer) {\n          this.renderer.dispose();\n          this.renderer.forceContextLoss();\n          if (this.renderer.domElement && this.renderer.domElement.parentNode) {\n            this.renderer.domElement.parentNode.removeChild(this.renderer.domElement);\n          }\n        }\n        if (this.composer) {\n          this.composer.dispose();\n        }\n\n        window.removeEventListener('resize', this.onWindowResize);\n        if (this.container) {\n          this.container.removeEventListener('mousedown', this.onMouseDown);\n          this.container.removeEventListener('mouseup', this.onMouseUp);\n          this.container.removeEventListener('mouseout', this.onMouseUp);\n\n          this.container.removeEventListener('touchstart', this.onTouchStart);\n          this.container.removeEventListener('touchend', this.onTouchEnd);\n          this.container.removeEventListener('touchcancel', this.onTouchEnd);\n          this.container.removeEventListener('contextmenu', this.onContextMenu);\n        }\n      }\n\n      setSize(width, height, updateStyles) {\n        if (width <= 0 || height <= 0) {\n          this.hasValidSize = false;\n          return;\n        }\n        this.composer.setSize(width, height, updateStyles);\n        this.hasValidSize = true;\n      }\n\n      tick() {\n        if (this.disposed) return;\n\n        if (!this.hasValidSize) {\n          const w = this.container.offsetWidth;\n          const h = this.container.offsetHeight;\n          if (w > 0 && h > 0) {\n            this.renderer.setSize(w, h, false);\n            this.camera.aspect = w / h;\n            this.camera.updateProjectionMatrix();\n            this.composer.setSize(w, h);\n            this.hasValidSize = true;\n            this.timer.reset();\n          } else {\n            requestAnimationFrame(this.tick);\n            return;\n          }\n        }\n\n        if (resizeRendererToDisplaySize(this.renderer, this.setSize)) {\n          const canvas = this.renderer.domElement;\n          if (this.hasValidSize) {\n            this.camera.aspect = canvas.clientWidth / canvas.clientHeight;\n            this.camera.updateProjectionMatrix();\n          }\n        }\n\n        if (this.hasValidSize) {\n          this.timer.update();\n          const delta = this.timer.getDelta();\n          this.render(delta);\n          this.update(delta);\n        }\n\n        requestAnimationFrame(this.tick);\n      }\n    }\n\n    const distortion_uniforms = {\n      uDistortionX: { value: new THREE.Vector2(80, 3) },\n      uDistortionY: { value: new THREE.Vector2(-40, 2.5) }\n    };\n\n    const distortion_vertex = `\n      #define PI 3.14159265358979\n      uniform vec2 uDistortionX;\n      uniform vec2 uDistortionY;\n      float nsin(float val){\n        return sin(val) * 0.5 + 0.5;\n      }\n      vec3 getDistortion(float progress){\n        progress = clamp(progress, 0., 1.);\n        float xAmp = uDistortionX.r;\n        float xFreq = uDistortionX.g;\n        float yAmp = uDistortionY.r;\n        float yFreq = uDistortionY.g;\n        return vec3( \n          xAmp * nsin(progress * PI * xFreq - PI / 2.),\n          yAmp * nsin(progress * PI * yFreq - PI / 2.),\n          0.\n        );\n      }\n    `;\n\n    const random = base => {\n      if (Array.isArray(base)) return Math.random() * (base[1] - base[0]) + base[0];\n      return Math.random() * base;\n    };\n\n    const pickRandom = arr => {\n      if (Array.isArray(arr)) return arr[Math.floor(Math.random() * arr.length)];\n      return arr;\n    };\n\n    function lerp(current, target, speed = 0.1, limit = 0.001) {\n      let change = (target - current) * speed;\n      if (Math.abs(change) < limit) {\n        change = target - current;\n      }\n      return change;\n    }\n\n    class CarLights {\n      constructor(webgl, options, colors, speed, fade) {\n        this.webgl = webgl;\n        this.options = options;\n        this.colors = colors;\n        this.speed = speed;\n        this.fade = fade;\n      }\n\n      init() {\n        const options = this.options;\n        let curve = new THREE.LineCurve3(new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0, -1));\n        let geometry = new THREE.TubeGeometry(curve, 40, 1, 8, false);\n\n        let instanced = new THREE.InstancedBufferGeometry().copy(geometry);\n        instanced.instanceCount = options.lightPairsPerRoadWay * 2;\n\n        let laneWidth = options.roadWidth / options.lanesPerRoad;\n\n        let aOffset = [];\n        let aMetrics = [];\n        let aColor = [];\n\n        let colors = this.colors;\n        if (Array.isArray(colors)) {\n          colors = colors.map(c => new THREE.Color(c));\n        } else {\n          colors = new THREE.Color(colors);\n        }\n\n        for (let i = 0; i < options.lightPairsPerRoadWay; i++) {\n          let radius = random(options.carLightsRadius);\n          let length = random(options.carLightsLength);\n          let speed = random(this.speed);\n\n          let carLane = i % options.lanesPerRoad;\n          let laneX = carLane * laneWidth - options.roadWidth / 2 + laneWidth / 2;\n\n          let carWidth = random(options.carWidthPercentage) * laneWidth;\n          let carShiftX = random(options.carShiftX) * laneWidth;\n          laneX += carShiftX;\n\n          let offsetY = random(options.carFloorSeparation) + radius * 1.3;\n\n          let offsetZ = -random(options.length);\n\n          aOffset.push(laneX - carWidth / 2);\n          aOffset.push(offsetY);\n          aOffset.push(offsetZ);\n\n          aOffset.push(laneX + carWidth / 2);\n          aOffset.push(offsetY);\n          aOffset.push(offsetZ);\n\n          aMetrics.push(radius);\n          aMetrics.push(length);\n          aMetrics.push(speed);\n\n          aMetrics.push(radius);\n          aMetrics.push(length);\n          aMetrics.push(speed);\n\n          let color = pickRandom(colors);\n          aColor.push(color.r);\n          aColor.push(color.g);\n          aColor.push(color.b);\n\n          aColor.push(color.r);\n          aColor.push(color.g);\n          aColor.push(color.b);\n        }\n\n        instanced.setAttribute('aOffset', new THREE.InstancedBufferAttribute(new Float32Array(aOffset), 3, false));\n        instanced.setAttribute('aMetrics', new THREE.InstancedBufferAttribute(new Float32Array(aMetrics), 3, false));\n        instanced.setAttribute('aColor', new THREE.InstancedBufferAttribute(new Float32Array(aColor), 3, false));\n\n        let material = new THREE.ShaderMaterial({\n          fragmentShader: carLightsFragment,\n          vertexShader: carLightsVertex,\n          transparent: true,\n          uniforms: Object.assign(\n            {\n              uTime: { value: 0 },\n              uTravelLength: { value: options.length },\n              uFade: { value: this.fade }\n            },\n            this.webgl.fogUniforms,\n            options.distortion.uniforms\n          )\n        });\n\n        material.onBeforeCompile = shader => {\n          shader.vertexShader = shader.vertexShader.replace(\n            '#include <getDistortion_vertex>',\n            options.distortion.getDistortion\n          );\n        };\n\n        let mesh = new THREE.Mesh(instanced, material);\n        mesh.frustumCulled = false;\n        this.webgl.scene.add(mesh);\n        this.mesh = mesh;\n      }\n\n      update(time) {\n        this.mesh.material.uniforms.uTime.value = time;\n      }\n    }\n\n    const carLightsFragment = `\n      #define USE_FOG;\n      ${THREE.ShaderChunk['fog_pars_fragment']}\n      varying vec3 vColor;\n      varying vec2 vUv; \n      uniform vec2 uFade;\n      void main() {\n        vec3 color = vec3(vColor);\n        float alpha = smoothstep(uFade.x, uFade.y, vUv.x);\n        gl_FragColor = vec4(color, alpha);\n        if (gl_FragColor.a < 0.0001) discard;\n        ${THREE.ShaderChunk['fog_fragment']}\n      }\n    `;\n\n    const carLightsVertex = `\n      #define USE_FOG;\n      ${THREE.ShaderChunk['fog_pars_vertex']}\n      attribute vec3 aOffset;\n      attribute vec3 aMetrics;\n      attribute vec3 aColor;\n      uniform float uTravelLength;\n      uniform float uTime;\n      varying vec2 vUv; \n      varying vec3 vColor; \n      #include <getDistortion_vertex>\n      void main() {\n        vec3 transformed = position.xyz;\n        float radius = aMetrics.r;\n        float myLength = aMetrics.g;\n        float speed = aMetrics.b;\n\n        transformed.xy *= radius;\n        transformed.z *= myLength;\n\n        transformed.z += myLength - mod(uTime * speed + aOffset.z, uTravelLength);\n        transformed.xy += aOffset.xy;\n\n        float progress = abs(transformed.z / uTravelLength);\n        transformed.xyz += getDistortion(progress);\n\n        vec4 mvPosition = modelViewMatrix * vec4(transformed, 1.);\n        gl_Position = projectionMatrix * mvPosition;\n        vUv = uv;\n        vColor = aColor;\n        ${THREE.ShaderChunk['fog_vertex']}\n      }\n    `;\n\n    class LightsSticks {\n      constructor(webgl, options) {\n        this.webgl = webgl;\n        this.options = options;\n      }\n\n      init() {\n        const options = this.options;\n        const geometry = new THREE.PlaneGeometry(1, 1);\n        let instanced = new THREE.InstancedBufferGeometry().copy(geometry);\n        let totalSticks = options.totalSideLightSticks;\n        instanced.instanceCount = totalSticks;\n\n        let stickoffset = options.length / (totalSticks - 1);\n        const aOffset = [];\n        const aColor = [];\n        const aMetrics = [];\n\n        let colors = options.colors.sticks;\n        if (Array.isArray(colors)) {\n          colors = colors.map(c => new THREE.Color(c));\n        } else {\n          colors = new THREE.Color(colors);\n        }\n\n        for (let i = 0; i < totalSticks; i++) {\n          let width = random(options.lightStickWidth);\n          let height = random(options.lightStickHeight);\n          aOffset.push((i - 1) * stickoffset * 2 + stickoffset * Math.random());\n\n          let color = pickRandom(colors);\n          aColor.push(color.r);\n          aColor.push(color.g);\n          aColor.push(color.b);\n\n          aMetrics.push(width);\n          aMetrics.push(height);\n        }\n\n        instanced.setAttribute('aOffset', new THREE.InstancedBufferAttribute(new Float32Array(aOffset), 1, false));\n        instanced.setAttribute('aColor', new THREE.InstancedBufferAttribute(new Float32Array(aColor), 3, false));\n        instanced.setAttribute('aMetrics', new THREE.InstancedBufferAttribute(new Float32Array(aMetrics), 2, false));\n\n        const material = new THREE.ShaderMaterial({\n          fragmentShader: sideSticksFragment,\n          vertexShader: sideSticksVertex,\n          side: THREE.DoubleSide,\n          uniforms: Object.assign(\n            {\n              uTravelLength: { value: options.length },\n              uTime: { value: 0 }\n            },\n            this.webgl.fogUniforms,\n            options.distortion.uniforms\n          )\n        });\n\n        material.onBeforeCompile = shader => {\n          shader.vertexShader = shader.vertexShader.replace(\n            '#include <getDistortion_vertex>',\n            options.distortion.getDistortion\n          );\n        };\n\n        const mesh = new THREE.Mesh(instanced, material);\n        mesh.frustumCulled = false;\n        this.webgl.scene.add(mesh);\n        this.mesh = mesh;\n      }\n\n      update(time) {\n        this.mesh.material.uniforms.uTime.value = time;\n      }\n    }\n\n    const sideSticksVertex = `\n      #define USE_FOG;\n      ${THREE.ShaderChunk['fog_pars_vertex']}\n      attribute float aOffset;\n      attribute vec3 aColor;\n      attribute vec2 aMetrics;\n      uniform float uTravelLength;\n      uniform float uTime;\n      varying vec3 vColor;\n      mat4 rotationY( in float angle ) {\n        return mat4(\tcos(angle),\t\t0,\t\tsin(angle),\t0,\n                     0,\t\t1.0,\t\t\t 0,\t0,\n                -sin(angle),\t0,\t\tcos(angle),\t0,\n                0, \t\t0,\t\t\t\t0,\t1);\n      }\n      #include <getDistortion_vertex>\n      void main(){\n        vec3 transformed = position.xyz;\n        float width = aMetrics.x;\n        float height = aMetrics.y;\n\n        transformed.xy *= vec2(width, height);\n        float time = mod(uTime * 60. * 2. + aOffset, uTravelLength);\n\n        transformed = (rotationY(3.14/2.) * vec4(transformed,1.)).xyz;\n\n        transformed.z += - uTravelLength + time;\n\n        float progress = abs(transformed.z / uTravelLength);\n        transformed.xyz += getDistortion(progress);\n\n        transformed.y += height / 2.;\n        transformed.x += -width / 2.;\n        vec4 mvPosition = modelViewMatrix * vec4(transformed, 1.);\n        gl_Position = projectionMatrix * mvPosition;\n        vColor = aColor;\n        ${THREE.ShaderChunk['fog_vertex']}\n      }\n    `;\n\n    const sideSticksFragment = `\n      #define USE_FOG;\n      ${THREE.ShaderChunk['fog_pars_fragment']}\n      varying vec3 vColor;\n      void main(){\n        vec3 color = vec3(vColor);\n        gl_FragColor = vec4(color,1.);\n        ${THREE.ShaderChunk['fog_fragment']}\n      }\n    `;\n\n    class Road {\n      constructor(webgl, options) {\n        this.webgl = webgl;\n        this.options = options;\n        this.uTime = { value: 0 };\n      }\n\n      createPlane(side, width, isRoad) {\n        const options = this.options;\n        let segments = 100;\n        const geometry = new THREE.PlaneGeometry(\n          isRoad ? options.roadWidth : options.islandWidth,\n          options.length,\n          20,\n          segments\n        );\n        let uniforms = {\n          uTravelLength: { value: options.length },\n          uColor: { value: new THREE.Color(isRoad ? options.colors.roadColor : options.colors.islandColor) },\n          uTime: this.uTime\n        };\n\n        if (isRoad) {\n          uniforms = Object.assign(uniforms, {\n            uLanes: { value: options.lanesPerRoad },\n            uBrokenLinesColor: { value: new THREE.Color(options.colors.brokenLines) },\n            uShoulderLinesColor: { value: new THREE.Color(options.colors.shoulderLines) },\n            uShoulderLinesWidthPercentage: { value: options.shoulderLinesWidthPercentage },\n            uBrokenLinesLengthPercentage: { value: options.brokenLinesLengthPercentage },\n            uBrokenLinesWidthPercentage: { value: options.brokenLinesWidthPercentage }\n          });\n        }\n\n        const material = new THREE.ShaderMaterial({\n          fragmentShader: isRoad ? roadFragment : islandFragment,\n          vertexShader: roadVertex,\n          side: THREE.DoubleSide,\n          uniforms: Object.assign(uniforms, this.webgl.fogUniforms, options.distortion.uniforms)\n        });\n\n        material.onBeforeCompile = shader => {\n          shader.vertexShader = shader.vertexShader.replace(\n            '#include <getDistortion_vertex>',\n            options.distortion.getDistortion\n          );\n        };\n\n        const mesh = new THREE.Mesh(geometry, material);\n        mesh.rotation.x = -Math.PI / 2;\n        mesh.position.z = -options.length / 2;\n        mesh.position.x += (this.options.islandWidth / 2 + options.roadWidth / 2) * side;\n        this.webgl.scene.add(mesh);\n\n        return mesh;\n      }\n\n      init() {\n        this.leftRoadWay = this.createPlane(-1, this.options.roadWidth, true);\n        this.rightRoadWay = this.createPlane(1, this.options.roadWidth, true);\n        this.island = this.createPlane(0, this.options.islandWidth, false);\n      }\n\n      update(time) {\n        this.uTime.value = time;\n      }\n    }\n\n    const roadBaseFragment = `\n      #define USE_FOG;\n      varying vec2 vUv; \n      uniform vec3 uColor;\n      uniform float uTime;\n      #include <roadMarkings_vars>\n      ${THREE.ShaderChunk['fog_pars_fragment']}\n      void main() {\n        vec2 uv = vUv;\n        vec3 color = vec3(uColor);\n        #include <roadMarkings_fragment>\n        gl_FragColor = vec4(color, 1.);\n        ${THREE.ShaderChunk['fog_fragment']}\n      }\n    `;\n\n    const islandFragment = roadBaseFragment\n      .replace('#include <roadMarkings_fragment>', '')\n      .replace('#include <roadMarkings_vars>', '');\n\n    const roadMarkings_vars = `\n      uniform float uLanes;\n      uniform vec3 uBrokenLinesColor;\n      uniform vec3 uShoulderLinesColor;\n      uniform float uShoulderLinesWidthPercentage;\n      uniform float uBrokenLinesWidthPercentage;\n      uniform float uBrokenLinesLengthPercentage;\n      highp float random(vec2 co) {\n        highp float a = 12.9898;\n        highp float b = 78.233;\n        highp float c = 43758.5453;\n        highp float dt = dot(co.xy, vec2(a, b));\n        highp float sn = mod(dt, 3.14);\n        return fract(sin(sn) * c);\n      }\n    `;\n\n    const roadMarkings_fragment = `\n      uv.y = mod(uv.y + uTime * 0.05, 1.);\n      float laneWidth = 1.0 / uLanes;\n      float brokenLineWidth = laneWidth * uBrokenLinesWidthPercentage;\n      float laneEmptySpace = 1. - uBrokenLinesLengthPercentage;\n\n      float brokenLines = step(1.0 - brokenLineWidth, fract(uv.x * 2.0)) * step(laneEmptySpace, fract(uv.y * 10.0));\n      float sideLines = step(1.0 - brokenLineWidth, fract((uv.x - laneWidth * (uLanes - 1.0)) * 2.0)) + step(brokenLineWidth, uv.x);\n\n      brokenLines = mix(brokenLines, sideLines, uv.x);\n    `;\n\n    const roadFragment = roadBaseFragment\n      .replace('#include <roadMarkings_fragment>', roadMarkings_fragment)\n      .replace('#include <roadMarkings_vars>', roadMarkings_vars);\n\n    const roadVertex = `\n      #define USE_FOG;\n      uniform float uTime;\n      ${THREE.ShaderChunk['fog_pars_vertex']}\n      uniform float uTravelLength;\n      varying vec2 vUv; \n      #include <getDistortion_vertex>\n      void main() {\n        vec3 transformed = position.xyz;\n        vec3 distortion = getDistortion((transformed.y + uTravelLength / 2.) / uTravelLength);\n        transformed.x += distortion.x;\n        transformed.z += distortion.y;\n        transformed.y += -1. * distortion.z;  \n        \n        vec4 mvPosition = modelViewMatrix * vec4(transformed, 1.);\n        gl_Position = projectionMatrix * mvPosition;\n        vUv = uv;\n        ${THREE.ShaderChunk['fog_vertex']}\n      }\n    `;\n\n    function resizeRendererToDisplaySize(renderer, setSize) {\n      const canvas = renderer.domElement;\n      const width = canvas.clientWidth;\n      const height = canvas.clientHeight;\n      if (width <= 0 || height <= 0) return false;\n      const needResize = canvas.width !== width || canvas.height !== height;\n      if (needResize) {\n        setSize(width, height, false);\n      }\n      return needResize;\n    }\n\n    const container = hyperspeed.current;\n    if (!container) return;\n\n    const options = {\n      ...DEFAULT_EFFECT_OPTIONS,\n      ...effectOptions,\n      colors: {\n        ...DEFAULT_EFFECT_OPTIONS.colors,\n        ...effectOptions.colors,\n        ...(lightMode\n          ? {\n              roadColor: 0xffffff,\n              islandColor: 0xf8f7fa,\n              background: 0xffffff,\n              shoulderLines: 0x7c3aed,\n              brokenLines: 0xc084fc\n            }\n          : {})\n      }\n    };\n    options.distortion = distortions[options.distortion];\n\n    const myApp = new App(container, options);\n    appRef.current = myApp;\n    myApp.loadAssets().then(myApp.init);\n\n    return () => {\n      if (appRef.current) {\n        appRef.current.dispose();\n        appRef.current = null;\n      }\n    };\n  }, [effectOptions, lightMode]);\n\n  return <div id=\"lights\" ref={hyperspeed}></div>;\n};\n\nexport default Hyperspeed;\n"
		}
	],
	"registryDependencies": [],
	"dependencies": [
		"postprocessing@^6.36.0",
		"three@^0.180.0"
	]
}