{
	"$schema": "https://ui.shadcn.com/schema/registry-item.json",
	"name": "Hyperspeed-TS-CSS",
	"title": "Hyperspeed",
	"description": "Animated lines continuously moving to simulate hyperspace travel on click hold.",
	"type": "registry:component",
	"files": [
		{
			"type": "registry:component",
			"path": "HyperSpeedPresets.ts",
			"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.tsx",
			"content": "import { BloomEffect, EffectComposer, EffectPass, RenderPass, SMAAEffect, SMAAPreset } from 'postprocessing';\nimport { type FC, useEffect, useRef } from 'react';\nimport * as THREE from 'three';\n\nimport './Hyperspeed.css';\n\ninterface Distortion {\n  uniforms: Record<string, { value: any }>;\n  getDistortion: string;\n  getJS?: (progress: number, time: number) => THREE.Vector3;\n}\n\ninterface Distortions {\n  [key: string]: Distortion;\n}\n\ninterface Colors {\n  roadColor: number;\n  islandColor: number;\n  background: number;\n  shoulderLines: number;\n  brokenLines: number;\n  leftCars: number[];\n  rightCars: number[];\n  sticks: number;\n}\n\ninterface HyperspeedOptions {\n  onSpeedUp?: (ev: MouseEvent | TouchEvent) => void;\n  onSlowDown?: (ev: MouseEvent | TouchEvent) => void;\n  distortion?: string | Distortion;\n  length: number;\n  roadWidth: number;\n  islandWidth: number;\n  lanesPerRoad: number;\n  fov: number;\n  fovSpeedUp: number;\n  speedUp: number;\n  carLightsFade: number;\n  totalSideLightSticks: number;\n  lightPairsPerRoadWay: number;\n  shoulderLinesWidthPercentage: number;\n  brokenLinesWidthPercentage: number;\n  brokenLinesLengthPercentage: number;\n  lightStickWidth: [number, number];\n  lightStickHeight: [number, number];\n  movingAwaySpeed: [number, number];\n  movingCloserSpeed: [number, number];\n  carLightsLength: [number, number];\n  carLightsRadius: [number, number];\n  carWidthPercentage: [number, number];\n  carShiftX: [number, number];\n  carFloorSeparation: [number, number];\n  colors: Colors;\n  isHyper?: boolean;\n}\n\ninterface HyperspeedProps {\n  effectOptions?: Partial<HyperspeedOptions>;\n  lightMode?: boolean;\n}\n\nconst defaultOptions: HyperspeedOptions = {\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\nfunction nsin(val: number) {\n  return Math.sin(val) * 0.5 + 0.5;\n}\n\nconst mountainUniforms = {\n  uFreq: { value: new THREE.Vector3(3, 6, 10) },\n  uAmp: { value: new THREE.Vector3(30, 30, 20) }\n};\n\nconst xyUniforms = {\n  uFreq: { value: new THREE.Vector2(5, 2) },\n  uAmp: { value: new THREE.Vector2(25, 15) }\n};\n\nconst LongRaceUniforms = {\n  uFreq: { value: new THREE.Vector2(2, 3) },\n  uAmp: { value: new THREE.Vector2(35, 10) }\n};\n\nconst turbulentUniforms = {\n  uFreq: { value: new THREE.Vector4(4, 8, 8, 1) },\n  uAmp: { value: new THREE.Vector4(25, 5, 10, 10) }\n};\n\nconst 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\nconst distortions: 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: number, time: number) => {\n      const movementProgressFix = 0.02;\n      const uFreq = mountainUniforms.uFreq.value;\n      const uAmp = mountainUniforms.uAmp.value;\n      const 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      const lookAtAmp = new THREE.Vector3(2, 2, 2);\n      const 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: number, time: number) => {\n      const movementProgressFix = 0.02;\n      const uFreq = xyUniforms.uFreq.value;\n      const uAmp = xyUniforms.uAmp.value;\n      const 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      const lookAtAmp = new THREE.Vector3(2, 0.4, 1);\n      const 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: number, time: number) => {\n      const camProgress = 0.0125;\n      const uFreq = LongRaceUniforms.uFreq.value;\n      const uAmp = LongRaceUniforms.uAmp.value;\n      const 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      const lookAtAmp = new THREE.Vector3(1, 1, 0);\n      const 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: number, time: number) => {\n      const uFreq = turbulentUniforms.uFreq.value;\n      const uAmp = turbulentUniforms.uAmp.value;\n\n      const getX = (p: number) =>\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: number) =>\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      const distortion = new THREE.Vector3(\n        getX(progress) - getX(progress + 0.007),\n        getY(progress) - getY(progress + 0.007),\n        0\n      );\n      const lookAtAmp = new THREE.Vector3(-2, -5, 0);\n      const 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: number, time: number) => {\n      const uFreq = deepUniforms.uFreq.value;\n      const uAmp = deepUniforms.uAmp.value;\n      const uPowY = deepUniforms.uPowY.value;\n\n      const getX = (p: number) => Math.sin(p * Math.PI * uFreq.x + time) * uAmp.x;\n      const getY = (p: number) => Math.pow(p * uPowY.x, uPowY.y) + Math.sin(p * Math.PI * uFreq.y + time) * uAmp.y;\n\n      const distortion = new THREE.Vector3(\n        getX(progress) - getX(progress + 0.01),\n        getY(progress) - getY(progress + 0.01),\n        0\n      );\n      const lookAtAmp = new THREE.Vector3(-2, -4, 0);\n      const lookAtOffset = new THREE.Vector3(0, 0, -10);\n      return distortion.multiply(lookAtAmp).add(lookAtOffset);\n    }\n  }\n};\n\nconst distortion_uniforms = {\n  uDistortionX: { value: new THREE.Vector2(80, 3) },\n  uDistortionY: { value: new THREE.Vector2(-40, 2.5) }\n};\n\nconst 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\nfunction random(base: number | [number, number]): number {\n  if (Array.isArray(base)) {\n    return Math.random() * (base[1] - base[0]) + base[0];\n  }\n  return Math.random() * base;\n}\n\nfunction pickRandom<T>(arr: T | T[]): T {\n  if (Array.isArray(arr)) {\n    return arr[Math.floor(Math.random() * arr.length)];\n  }\n  return arr;\n}\n\nfunction lerp(current: number, target: number, speed = 0.1, limit = 0.001): number {\n  let change = (target - current) * speed;\n  if (Math.abs(change) < limit) {\n    change = target - current;\n  }\n  return change;\n}\n\nclass CarLights {\n  webgl: App;\n  options: HyperspeedOptions;\n  colors: number[] | THREE.Color;\n  speed: [number, number];\n  fade: THREE.Vector2;\n  mesh!: THREE.Mesh<THREE.InstancedBufferGeometry, THREE.ShaderMaterial>;\n\n  constructor(\n    webgl: App,\n    options: HyperspeedOptions,\n    colors: number[] | THREE.Color,\n    speed: [number, number],\n    fade: THREE.Vector2\n  ) {\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    const curve = new THREE.LineCurve3(new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0, -1));\n    const geometry = new THREE.TubeGeometry(curve, 40, 1, 8, false);\n\n    const instanced = new THREE.InstancedBufferGeometry().copy(geometry as any) as THREE.InstancedBufferGeometry;\n    instanced.instanceCount = options.lightPairsPerRoadWay * 2;\n\n    const laneWidth = options.roadWidth / options.lanesPerRoad;\n\n    const aOffset: number[] = [];\n    const aMetrics: number[] = [];\n    const aColor: number[] = [];\n\n    let colorArray: THREE.Color[];\n    if (Array.isArray(this.colors)) {\n      colorArray = this.colors.map(c => new THREE.Color(c));\n    } else {\n      colorArray = [new THREE.Color(this.colors)];\n    }\n\n    for (let i = 0; i < options.lightPairsPerRoadWay; i++) {\n      const radius = random(options.carLightsRadius);\n      const length = random(options.carLightsLength);\n      const spd = random(this.speed);\n\n      const carLane = i % options.lanesPerRoad;\n      let laneX = carLane * laneWidth - options.roadWidth / 2 + laneWidth / 2;\n\n      const carWidth = random(options.carWidthPercentage) * laneWidth;\n      const carShiftX = random(options.carShiftX) * laneWidth;\n      laneX += carShiftX;\n\n      const offsetY = random(options.carFloorSeparation) + radius * 1.3;\n      const 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(spd);\n\n      aMetrics.push(radius);\n      aMetrics.push(length);\n      aMetrics.push(spd);\n\n      const color = pickRandom<THREE.Color>(colorArray);\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    const 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        (typeof this.options.distortion === 'object' ? this.options.distortion.uniforms : {}) || {}\n      )\n    });\n\n    material.onBeforeCompile = shader => {\n      shader.vertexShader = shader.vertexShader.replace(\n        '#include <getDistortion_vertex>',\n        typeof this.options.distortion === 'object' ? this.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: number) {\n    if (this.mesh.material.uniforms.uTime) {\n      this.mesh.material.uniforms.uTime.value = time;\n    }\n  }\n}\n\nconst 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\nconst 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\nclass LightsSticks {\n  webgl: App;\n  options: HyperspeedOptions;\n  mesh!: THREE.Mesh<THREE.InstancedBufferGeometry, THREE.ShaderMaterial>;\n\n  constructor(webgl: App, options: HyperspeedOptions) {\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    const instanced = new THREE.InstancedBufferGeometry().copy(geometry as any) as THREE.InstancedBufferGeometry;\n    const totalSticks = options.totalSideLightSticks;\n    instanced.instanceCount = totalSticks;\n\n    const stickoffset = options.length / (totalSticks - 1);\n    const aOffset: number[] = [];\n    const aColor: number[] = [];\n    const aMetrics: number[] = [];\n\n    let colorArray: THREE.Color[];\n    if (Array.isArray(options.colors.sticks)) {\n      colorArray = options.colors.sticks.map(c => new THREE.Color(c));\n    } else {\n      colorArray = [new THREE.Color(options.colors.sticks)];\n    }\n\n    for (let i = 0; i < totalSticks; i++) {\n      const width = random(options.lightStickWidth);\n      const height = random(options.lightStickHeight);\n      aOffset.push((i - 1) * stickoffset * 2 + stickoffset * Math.random());\n\n      const color = pickRandom<THREE.Color>(colorArray);\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        (typeof options.distortion === 'object' ? options.distortion.uniforms : {}) || {}\n      )\n    });\n\n    material.onBeforeCompile = shader => {\n      shader.vertexShader = shader.vertexShader.replace(\n        '#include <getDistortion_vertex>',\n        typeof this.options.distortion === 'object' ? this.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: number) {\n    if (this.mesh.material.uniforms.uTime) {\n      this.mesh.material.uniforms.uTime.value = time;\n    }\n  }\n}\n\nconst 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(\n      cos(angle),\t\t0,\t\tsin(angle),\t0,\n      0,\t\t        1.0,\t0,\t\t\t0,\n      -sin(angle),\t    0,\t\tcos(angle),\t0,\n      0, \t\t        0,\t\t0,\t\t\t1\n    );\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    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\nconst 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\nclass Road {\n  webgl: App;\n  options: HyperspeedOptions;\n  uTime: { value: number };\n  leftRoadWay!: THREE.Mesh;\n  rightRoadWay!: THREE.Mesh;\n  island!: THREE.Mesh;\n\n  constructor(webgl: App, options: HyperspeedOptions) {\n    this.webgl = webgl;\n    this.options = options;\n    this.uTime = { value: 0 };\n  }\n\n  createPlane(side: number, width: number, isRoad: boolean) {\n    const options = this.options;\n    const segments = 100;\n    const geometry = new THREE.PlaneGeometry(\n      isRoad ? options.roadWidth : options.islandWidth,\n      options.length,\n      20,\n      segments\n    );\n\n    let uniforms: Record<string, { value: any }> = {\n      uTravelLength: { value: options.length },\n      uColor: {\n        value: new THREE.Color(isRoad ? options.colors.roadColor : options.colors.islandColor)\n      },\n      uTime: this.uTime\n    };\n\n    if (isRoad) {\n      uniforms = Object.assign(uniforms, {\n        uLanes: { value: options.lanesPerRoad },\n        uBrokenLinesColor: {\n          value: new THREE.Color(options.colors.brokenLines)\n        },\n        uShoulderLinesColor: {\n          value: new THREE.Color(options.colors.shoulderLines)\n        },\n        uShoulderLinesWidthPercentage: {\n          value: options.shoulderLinesWidthPercentage\n        },\n        uBrokenLinesLengthPercentage: {\n          value: options.brokenLinesLengthPercentage\n        },\n        uBrokenLinesWidthPercentage: {\n          value: options.brokenLinesWidthPercentage\n        }\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(\n        uniforms,\n        this.webgl.fogUniforms,\n        (typeof options.distortion === 'object' ? options.distortion.uniforms : {}) || {}\n      )\n    });\n\n    material.onBeforeCompile = shader => {\n      shader.vertexShader = shader.vertexShader.replace(\n        '#include <getDistortion_vertex>',\n        typeof this.options.distortion === 'object' ? this.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\n    this.webgl.scene.add(mesh);\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: number) {\n    this.uTime.value = time;\n  }\n}\n\nconst 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\nconst islandFragment = roadBaseFragment\n  .replace('#include <roadMarkings_fragment>', '')\n  .replace('#include <roadMarkings_vars>', '');\n\nconst 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\nconst 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\nconst roadFragment = roadBaseFragment\n  .replace('#include <roadMarkings_fragment>', roadMarkings_fragment)\n  .replace('#include <roadMarkings_vars>', roadMarkings_vars);\n\nconst 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\nfunction resizeRendererToDisplaySize(\n  renderer: THREE.WebGLRenderer,\n  setSize: (width: number, height: number, updateStyle: boolean) => void\n) {\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\nclass App {\n  container: HTMLElement;\n  options: HyperspeedOptions;\n  renderer: THREE.WebGLRenderer;\n  composer: EffectComposer;\n  camera: THREE.PerspectiveCamera;\n  scene: THREE.Scene;\n  renderPass!: RenderPass;\n  bloomPass!: EffectPass;\n  timer: THREE.Timer;\n  assets: Record<string, any>;\n  disposed: boolean;\n  road: Road;\n  leftCarLights: CarLights;\n  rightCarLights: CarLights;\n  leftSticks: LightsSticks;\n  fogUniforms: Record<string, { value: any }>;\n  fovTarget: number;\n  speedUpTarget: number;\n  speedUp: number;\n  timeOffset: number;\n  hasValidSize: boolean;\n\n  constructor(container: HTMLElement, options: HyperspeedOptions) {\n    this.options = options;\n    if (!this.options.distortion) {\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\n    this.composer = new EffectComposer(this.renderer);\n    container.appendChild(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\n    this.scene = new THREE.Scene();\n    this.scene.background = null;\n\n    const fog = new THREE.Fog(options.colors.background, options.length * 0.2, options.length * 500);\n    this.scene.fog = fog;\n\n    this.fogUniforms = {\n      fogColor: { value: fog.color },\n      fogNear: { value: fog.near },\n      fogFar: { value: fog.far }\n    };\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      })\n    );\n    this.renderPass.renderToScreen = false;\n    this.bloomPass.renderToScreen = false;\n    smaaPass.renderToScreen = true;\n\n    this.composer.addPass(this.renderPass);\n    this.composer.addPass(this.bloomPass);\n    this.composer.addPass(smaaPass);\n  }\n\n  loadAssets(): Promise<void> {\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\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\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    this.leftCarLights.mesh.position.setX(-options.roadWidth / 2 - options.islandWidth / 2);\n\n    this.rightCarLights.init();\n    this.rightCarLights.mesh.position.setX(options.roadWidth / 2 + options.islandWidth / 2);\n\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    this.container.addEventListener('contextmenu', this.onContextMenu);\n\n    this.tick();\n  }\n\n  onMouseDown(ev: MouseEvent) {\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: MouseEvent) {\n    if (this.options.onSlowDown) this.options.onSlowDown(ev);\n    this.fovTarget = this.options.fov;\n    this.speedUpTarget = 0;\n  }\n\n  onTouchStart(ev: TouchEvent) {\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: TouchEvent) {\n    if (this.options.onSlowDown) this.options.onSlowDown(ev);\n    this.fovTarget = this.options.fov;\n    this.speedUpTarget = 0;\n  }\n\n  onContextMenu(ev: MouseEvent) {\n    ev.preventDefault();\n  }\n\n  update(delta: number) {\n    const 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    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    const 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 (typeof this.options.distortion === 'object' && this.options.distortion.getJS) {\n      const distortion = this.options.distortion.getJS(0.025, time);\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\n    if (updateCamera) {\n      this.camera.updateProjectionMatrix();\n    }\n  }\n\n  render(delta: number) {\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 as unknown as THREE.Mesh;\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: number, height: number, updateStyles: boolean) {\n    this.composer.setSize(width, height, updateStyles);\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\nconst DEFAULT_EFFECT_OPTIONS: Partial<HyperspeedOptions> = {};\n\nconst Hyperspeed: FC<HyperspeedProps> = ({ effectOptions = DEFAULT_EFFECT_OPTIONS, lightMode = false }) => {\n  const hyperspeed = useRef<HTMLDivElement>(null);\n  const appRef = useRef<App | null>(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\n    const container = hyperspeed.current;\n    if (!container) return;\n\n    const options: HyperspeedOptions = {\n      ...defaultOptions,\n      ...effectOptions,\n      colors: { ...defaultOptions.colors, ...effectOptions.colors }\n    };\n    if (typeof options.distortion === 'string') {\n      options.distortion = distortions[options.distortion];\n    }\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      }\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"
	]
}