, `keyup
use-animation-demo - CodeSandbox, `keyuse-animation-demo - CodeSandbox, `mousemoveuse-animation-demo - CodeSandbox, `mousemovenormaliseduse-animation-demo - CodeSandbox and `mousemovedeltause-animation-demo - CodeSandbox.\n\n```tsx\nconst angle = useObservable(0)\n \n\u002F\u002F ...\n\nuseEffect(() =\u003E {\n const s = combineLatest([\n mousemovenormalised$.pipe(\n sampleTime(100),\n map(([x]) =\u003E Math.PI * 2 * Math.sin(x * Math.PI)),\n ),\n interval(100),\n ])\n .pipe(map(([angle, time]) =\u003E angle + time \u002F 5.0))\n .subscribe(angle.set)\n return () =\u003E s.unsubscribe()\n}, [angle])\n```\n","id":"mod_QtCPc6ka7UpSmVEfTLtZ6S","is_binary":false,"title":"README.md","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"SkcH6sHiWK","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":null},{"code":"https:\u002F\u002Frawcdn.githack.com\u002Fbfollington\u002Fuse-animation\u002F55eb38e591426d628c0ee6fe4a2494a3e42cedfe\u002Fsrc\u002Fresources\u002Fdither_matrix.png","id":"mod_T7RgP1zMKFpz2ZQddqeRuB","is_binary":true,"title":"dither_matrix.png","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"rJJMBpjHoZt","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"HyHH6jSsbt"},{"code":"https:\u002F\u002Frawcdn.githack.com\u002Fbfollington\u002Fuse-animation\u002F55eb38e591426d628c0ee6fe4a2494a3e42cedfe\u002Fsrc\u002Fresources\u002Fpalette_rising_sun.png","id":"mod_FA6yvZRYdDkai4dU885Tp2","is_binary":true,"title":"palette_rising_sun.png","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"BkxMSpsHs-Y","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"HyHH6jSsbt"},{"code":"https:\u002F\u002Frawcdn.githack.com\u002Fbfollington\u002Fuse-animation\u002F55eb38e591426d628c0ee6fe4a2494a3e42cedfe\u002Fsrc\u002Fresources\u002Fpalette_moonlight.png","id":"mod_L7jpbDa8R4M97VPrMSvY2C","is_binary":true,"title":"palette_moonlight.png","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"BkbzS6oSobt","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"HyHH6jSsbt"},{"code":"https:\u002F\u002Frawcdn.githack.com\u002Fbfollington\u002Fuse-animation\u002F55eb38e591426d628c0ee6fe4a2494a3e42cedfe\u002Fsrc\u002Fresources\u002Fblue_noise.png","id":"mod_9wVxKn2hZEBhBoovDkmuaJ","is_binary":true,"title":"blue_noise.png","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"HkMGHasrjZF","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"HyHH6jSsbt"},{"code":"https:\u002F\u002Frawcdn.githack.com\u002Fbfollington\u002Fuse-animation\u002F55eb38e591426d628c0ee6fe4a2494a3e42cedfe\u002Fsrc\u002Fresources\u002Fblade_diffuse.jpg","id":"mod_NEYt82X3PQjuxBnk8mhW2y","is_binary":true,"title":"blade_diffuse.jpg","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"ry7GH6iribK","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"HyHH6jSsbt"},{"code":"https:\u002F\u002Frawcdn.githack.com\u002Fbfollington\u002Fuse-animation\u002F55eb38e591426d628c0ee6fe4a2494a3e42cedfe\u002Fsrc\u002Fresources\u002Fseamless7.png","id":"mod_Nes6XToptUL9Brxib5gCht","is_binary":true,"title":"seamless7.png","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"B1VGHTjBoZt","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"HyHH6jSsbt"},{"code":"https:\u002F\u002Frawcdn.githack.com\u002Fbfollington\u002Fuse-animation\u002F55eb38e591426d628c0ee6fe4a2494a3e42cedfe\u002Fsrc\u002Fresources\u002Fpalette_twilight.png","id":"mod_8oHaMpGe8pvdqb1zzoqUqX","is_binary":true,"title":"palette_twilight.png","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"B1SGS6oriWF","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"HyHH6jSsbt"},{"code":"https:\u002F\u002Frawcdn.githack.com\u002Fbfollington\u002Fuse-animation\u002F55eb38e591426d628c0ee6fe4a2494a3e42cedfe\u002Fsrc\u002Fresources\u002Fblade_alpha.jpg","id":"mod_2nEfXkdpgtYbjkbR4ZNmxK","is_binary":true,"title":"blade_alpha.jpg","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"BJIzBTjrs-Y","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"HyHH6jSsbt"},{"code":"https:\u002F\u002Frawcdn.githack.com\u002Fbfollington\u002Fuse-animation\u002F55eb38e591426d628c0ee6fe4a2494a3e42cedfe\u002Fsrc\u002Fresources\u002Fwater-0397b.jpeg","id":"mod_VCUJDK2ARygYne9NSZb9VG","is_binary":true,"title":"water-0397b.jpeg","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"HyPGB6jrs-t","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"HyHH6jSsbt"},{"code":"https:\u002F\u002Frawcdn.githack.com\u002Fbfollington\u002Fuse-animation\u002F55eb38e591426d628c0ee6fe4a2494a3e42cedfe\u002Fsrc\u002Fresources\u002Fseamless6.png","id":"mod_LRPBExWjj9NLeVs1ZhNXv9","is_binary":true,"title":"seamless6.png","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"ByOzrpirjZt","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"HyHH6jSsbt"},{"code":"https:\u002F\u002Frawcdn.githack.com\u002Fbfollington\u002Fuse-animation\u002F55eb38e591426d628c0ee6fe4a2494a3e42cedfe\u002Fsrc\u002Fresources\u002Fseamless8.png","id":"mod_N2UP3wx8FZAPi8r1r24Q17","is_binary":true,"title":"seamless8.png","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"BkFfH6jrsbt","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"HyHH6jSsbt"},{"code":"https:\u002F\u002Frawcdn.githack.com\u002Fbfollington\u002Fuse-animation\u002F55eb38e591426d628c0ee6fe4a2494a3e42cedfe\u002Fsrc\u002Fresources\u002Fseamless5.png","id":"mod_TJRoLMBHjBVVMSN1uFx97E","is_binary":true,"title":"seamless5.png","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"BJcGHasSj-Y","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"HyHH6jSsbt"},{"code":"{\n \"extends\": \"react-app\",\n \"rules\": {\n \"semi\": [2, \"never\"],\n \"no-unexpected-multiline\": \"error\"\n }\n }","id":"mod_BMkiLrQSZa12gsiHsduPvW","is_binary":false,"title":".eslintrc","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"Bk8SaiSoWK","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":null},{"code":"node_modules\nyarn.lock\n","id":"mod_5kauhjmGFrWC1pce7LrU3B","is_binary":false,"title":".gitignore","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"BkDS6iBiWF","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":null},{"code":"{\n \"printWidth\": 160,\n \"tabWidth\": 2,\n \"useTabs\": false,\n \"semi\": false,\n \"singleQuote\": false,\n \"trailingComma\": \"all\",\n \"bracketSpacing\": true,\n \"jsxBracketSameLine\": true,\n \"fluid\": false\n}","id":"mod_G8wsjrQCSronfhZwvuD4Cq","is_binary":false,"title":".prettierrc","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"r1OHTjri-K","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":null},{"code":"import * as THREE from \"three\"\nimport { shaderMaterial } from \"@react-three\u002Fdrei\"\nimport { extend } from \"@react-three\u002Ffiber\"\n\nconst ShinyMaterial = shaderMaterial(\n { baseSpeed: 0.05, noiseTexture: null, noiseScale: 0.5337, alpha: 1.0, time: 10.0 },\n `\n varying vec2 vUv;\n void main() {\n vUv = uv;\n gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n }\n `,\n `\n uniform sampler2D baseTexture;\n uniform float baseSpeed;\n uniform sampler2D noiseTexture;\n uniform float noiseScale;\n uniform float alpha;\n uniform float time;\n uniform vec2 resolution;\n\n varying vec2 vUv;\n void main()\n {\n vec2 uvTimeShift = vUv + vec2( -0.3, 1.5 ) * time * baseSpeed;\n vec4 noiseGeneratorTimeShift = texture2D( noiseTexture, uvTimeShift );\n vec2 uvNoiseTimeShift = vUv + noiseScale * vec2( noiseGeneratorTimeShift.r, noiseGeneratorTimeShift.b );\n vec4 baseColor = texture2D( baseTexture, uvNoiseTimeShift );\n\n baseColor.a = alpha;\n gl_FragColor = baseColor;\n }\n `,\n (self) =\u003E {\n self.side = THREE.DoubleSide\n },\n)\n\nextend({ ShinyMaterial })\n","id":"mod_GZJB7LMBtBNCYp1ucrTG5C","is_binary":false,"title":"ShinyMaterial.js","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"HJIlBpoBoWt","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"ByZHpsHjbt"},{"code":"\u002F\u002F\u002F \u003Creference types=\"react-scripts\" \u002F\u003E\n","id":"mod_87sgUzhRinLEvK8StsjDXc","is_binary":false,"title":"react-app-env.d.ts","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"HkPlr6orsWK","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"HyxSToBs-F"},{"code":"import { Mesh, OrthographicCamera, PlaneBufferGeometry, Scene, ShaderMaterial, UniformsUtils, Vector2 } from \"three\"\nimport { DIMENSIONS } from \"..\u002Fconst\"\n\nexport const CrtShader = {\n uniforms: {\n tDiffuse: { value: null },\n iResolution: {\n type: \"vec2\",\n value: new Vector2(DIMENSIONS.width - 3, DIMENSIONS.height - 2),\n },\n },\n vertexShader: \u002F*glsl*\u002F `\n varying vec2 vUv;\n\n\t\tvoid main() {\n\t\t\tvUv = uv;\n\t\t\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\t\t}\n `,\n fragmentShader: \u002F*glsl*\u002F `\n \u002F\u002F #include \u003Ccommon\u003E\n\t\tuniform sampler2D tDiffuse;\n\t\tvarying vec2 vUv;\n uniform vec2 iResolution;\n\n \u002F\u002F\n\u002F\u002F PUBLIC DOMAIN CRT STYLED SCAN-LINE SHADER\n\u002F\u002F\n\u002F\u002F by Timothy Lottes\n\u002F\u002F\n\u002F\u002F This is more along the style of a really good CGA arcade monitor.\n\u002F\u002F With RGB inputs instead of NTSC.\n\u002F\u002F The shadow mask example has the mask rotated 90 degrees for less chromatic aberration.\n\u002F\u002F\n\u002F\u002F Left it unoptimized to show the theory behind the algorithm.\n\u002F\u002F\n\u002F\u002F It is an example what I personally would want as a display option for pixel art games.\n\u002F\u002F Please take and use, change, or whatever.\n\u002F\u002F\n\n\u002F\u002F Emulated input resolution.\n#if 0\n \u002F\u002F Fix resolution to set amount.\n #define res (vec2(320.0\u002F1.0,160.0\u002F1.0))\n#else\n \u002F\u002F Optimize for resize.\n #define res (iResolution.xy\u002F6.0)\n#endif\n\n\u002F\u002F Hardness of scanline.\n\u002F\u002F -8.0 = soft\n\u002F\u002F -16.0 = medium\nfloat hardScan=-8.0;\n\n\u002F\u002F Hardness of pixels in scanline.\n\u002F\u002F -2.0 = soft\n\u002F\u002F -4.0 = hard\nfloat hardPix=-3.0;\n\n\u002F\u002F Display warp.\n\u002F\u002F 0.0 = none\n\u002F\u002F 1.0\u002F8.0 = extreme\nvec2 warp=vec2(1.0\u002F32.0,1.0\u002F24.0); \n\n\u002F\u002F Amount of shadow mask.\nfloat maskDark=0.5;\nfloat maskLight=1.5;\n\n\u002F\u002F------------------------------------------------------------------------\n\n\u002F\u002F sRGB to Linear.\n\u002F\u002F Assuing using sRGB typed textures this should not be needed.\nfloat ToLinear1(float c){return(c\u003C=0.04045)?c\u002F12.92:pow((c+0.055)\u002F1.055,2.4);}\nvec3 ToLinear(vec3 c){return vec3(ToLinear1(c.r),ToLinear1(c.g),ToLinear1(c.b));}\n\n\u002F\u002F Linear to sRGB.\n\u002F\u002F Assuing using sRGB typed textures this should not be needed.\nfloat ToSrgb1(float c){return(c\u003C0.0031308?c*12.92:1.055*pow(c,0.41666)-0.055);}\nvec3 ToSrgb(vec3 c){return vec3(ToSrgb1(c.r),ToSrgb1(c.g),ToSrgb1(c.b));}\n\n\u002F\u002F Nearest emulated sample given floating point position and texel offset.\n\u002F\u002F Also zero's off screen.\nvec3 Fetch(vec2 pos,vec2 off){\n pos=floor(pos*res+off)\u002Fres;\n if(max(abs(pos.x-0.5),abs(pos.y-0.5))\u003E0.5)return vec3(0.0,0.0,0.0);\n return ToLinear(texture2D(tDiffuse,pos.xy,-16.0).rgb);}\n\n\u002F\u002F Distance in emulated pixels to nearest texel.\nvec2 Dist(vec2 pos){pos=pos*res;return -((pos-floor(pos))-vec2(0.5));}\n \n\u002F\u002F 1D Gaussian.\nfloat Gaus(float pos,float scale){return exp2(scale*pos*pos);}\n\n\u002F\u002F 3-tap Gaussian filter along horz line.\nvec3 Horz3(vec2 pos,float off){\n vec3 b=Fetch(pos,vec2(-1.0,off));\n vec3 c=Fetch(pos,vec2( 0.0,off));\n vec3 d=Fetch(pos,vec2( 1.0,off));\n float dst=Dist(pos).x;\n \u002F\u002F Convert distance to weight.\n float scale=hardPix;\n float wb=Gaus(dst-1.0,scale);\n float wc=Gaus(dst+0.0,scale);\n float wd=Gaus(dst+1.0,scale);\n \u002F\u002F Return filtered sample.\n return (d*wd)\u002F(wb+wc+wd);}\n\n\u002F\u002F 5-tap Gaussian filter along horz line.\nvec3 Horz5(vec2 pos,float off){\n vec3 a=Fetch(pos,vec2(-2.0,off));\n vec3 b=Fetch(pos,vec2(-1.0,off));\n vec3 c=Fetch(pos,vec2( 0.0,off));\n vec3 d=Fetch(pos,vec2( 1.0,off));\n vec3 e=Fetch(pos,vec2( 2.0,off));\n float dst=Dist(pos).x;\n \u002F\u002F Convert distance to weight.\n float scale=hardPix;\n float wa=Gaus(dst-2.0,scale);\n float wb=Gaus(dst-1.0,scale);\n float wc=Gaus(dst+0.0,scale);\n float wd=Gaus(dst+1.0,scale);\n float we=Gaus(dst+2.0,scale);\n \u002F\u002F Return filtered sample.\n return (a*wa+b*wb+c*wc+d*wd+e*we)\u002F(wa+wb+wc+wd+we);}\n\n\u002F\u002F Return scanline weight.\nfloat Scan(vec2 pos,float off){\n float dst=Dist(pos).y;\n return Gaus(dst+off,hardScan);}\n\n\u002F\u002F Allow nearest three lines to effect pixel.\nvec3 Tri(vec2 pos){\n vec3 a=Horz3(pos,-1.0);\n vec3 b=Horz5(pos, 0.0);\n vec3 c=Horz3(pos, 1.0);\n float wa=Scan(pos,-1.0);\n float wb=Scan(pos, 0.0);\n float wc=Scan(pos, 1.0);\n return a*wa+b*wb+c*wc;}\n\n\u002F\u002F Distortion of scanlines, and end of screen alpha.\nvec2 Warp(vec2 pos){\n pos=pos*2.0-1.0; \n pos*=vec2(1.0+(pos.y*pos.y)*warp.x,1.0+(pos.x*pos.x)*warp.y);\n return pos*0.5+0.5;}\n\n\u002F\u002F Shadow mask.\nvec3 Mask(vec2 pos){\n pos.x+=pos.y*3.0;\n\n vec3 mask=vec3(maskDark,maskDark,maskDark);\n pos.x=fract(pos.x\u002F6.0);\n\n\n\n if(pos.x\u003C0.333333333)mask.r=maskLight;\n else if(pos.x\u003C0.66666666)mask.g=maskLight;\n else mask.b=maskLight;\n return mask;} \n\n\u002F\u002F Draw dividing bars.\nfloat Bar(float pos,float bar){pos-=bar;return pos*pos\u003C4.0?0.0:1.0;}\n\n\u002F\u002F Entry.\nvoid main(){\n vec2 fragCoord = vUv * iResolution;\n\n vec2 pos=Warp(fragCoord.xy\u002FiResolution.xy+vec2(0.0,0.0));\n\n gl_FragColor.rgb=Tri(pos)*Mask(fragCoord.xy);\n\n gl_FragColor.a=1.0; \n gl_FragColor.rgb=ToSrgb(gl_FragColor.rgb);\n\n }\n\n \n\n\n `,\n}\n","id":"mod_DKSAhibEL4R86f2QoMUFP2","is_binary":false,"title":"CrtShader.js","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"ByuxHTiBj-Y","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"SJMBpiBsWt"},{"code":"export const DitherShader = {\n uniforms: {\n tDiffuse: { type: \"t\", value: null },\n tDither: { type: \"t\", value: null },\n tPalette: { type: \"t\", value: null },\n\n u_bit_depth: { type: \"i\", value: 32 },\n u_contrast: { value: 1 },\n u_offset: { value: 0 },\n u_dither_size: { type: \"i\", value: 2 },\n },\n vertexShader: \u002F*glsl*\u002F `\n varying vec2 vUv;\n\n\t\tvoid main() {\n\t\t\tvUv = uv;\n\t\t\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\t\t}\n `,\n fragmentShader: \u002F*glsl*\u002F `\n \u002F* \n This shader is under MIT license. Feel free to use, improve and \n change this shader according to your needs and consider sharing \n the modified result to godotshaders.com.\n *\u002F\n\n uniform sampler2D tDiffuse;\n\t\tvarying vec2 vUv;\n\n uniform sampler2D tDither;\n uniform sampler2D tPalette;\n\n uniform int u_bit_depth;\n uniform float u_contrast;\n uniform float u_offset;\n uniform int u_dither_size;\n\n void main() \n {\n \u002F\u002F sample the screen texture at the desired output resolution (according to u_dither_size)\n \u002F\u002F this will effectively pixelate the resulting output\n vec2 screen_size = vec2(textureSize(tDiffuse, 0)) \u002F float(u_dither_size);\n vec2 screen_sample_uv = floor(vUv * screen_size) \u002F screen_size;\n vec3 screen_col = texture(tDiffuse, screen_sample_uv).rgb;\n \n \u002F\u002F calculate pixel luminosity (https:\u002F\u002Fstackoverflow.com\u002Fquestions\u002F596216\u002Fformula-to-determine-brightness-of-rgb-color)\n float lum = (screen_col.r * 0.299) + (screen_col.g * 0.587) + (screen_col.b * 0.114);\n \n \u002F\u002F adjust with contrast and offset parameters\n float contrast = u_contrast;\n lum = (lum - 0.5 + u_offset) * contrast + 0.5;\n lum = clamp(lum, 0.0, 1.0);\n \n \u002F\u002F reduce luminosity bit depth to give a more banded visual if desired\t\n float bits = float(u_bit_depth);\n lum = floor(lum * bits) \u002F bits;\n \n \u002F\u002F to support multicolour palettes, we want to dither between the two colours on the palette\n \u002F\u002F which are adjacent to the current pixel luminosity.\n \u002F\u002F to do this, we need to determine which 'band' lum falls into, calculate the upper and lower\n \u002F\u002F bound of that band, then later we will use the dither texture to pick either the upper or \n \u002F\u002F lower colour.\n \n \u002F\u002F get the palette texture size mapped so it is 1px high (so the x value however many colour bands there are)\n ivec2 col_size = textureSize(tPalette, 0);\n ivec2 test = col_size;\n col_size \u002F= col_size.y;\n \n float col_x = float(col_size.x) - 1.0; \u002F\u002F colour boundaries is 1 less than the number of colour bands\n float col_texel_size = 1.0 \u002F col_x; \u002F\u002F the size of one colour boundary\n \n lum = max(lum - 0.00001, 0.0); \u002F\u002F makes sure our floor calculation below behaves when lum == 1.0\n float lum_lower = floor(lum * col_x) * col_texel_size;\n float lum_upper = (floor(lum * col_x) + 1.0) * col_texel_size;\n float lum_scaled = lum * col_x - floor(lum * col_x); \u002F\u002F calculates where lum lies between the upper and lower bound\n \n \u002F\u002F map the dither texture onto the screen. there are better ways of doing this that makes the dither pattern 'stick'\n \u002F\u002F with objects in the 3D world, instead of being mapped onto the screen. see lucas pope's details posts on how he \n \u002F\u002F achieved this in Obra Dinn: https:\u002F\u002Fforums.tigsource.com\u002Findex.php?topic=40832.msg1363742#msg1363742\n ivec2 noise_size = textureSize(tDither, 0);\n vec2 inv_noise_size = vec2(1.0 \u002F float(noise_size.x), 1.0 \u002F float(noise_size.y));\n vec2 noise_uv = vUv * inv_noise_size * vec2(float(screen_size.x), float(screen_size.y));\n float threshold = texture(tDither, noise_uv).r;\n \n \u002F\u002F adjust the dither slightly so min and max aren't quite at 0.0 and 1.0\n \u002F\u002F otherwise we wouldn't get fullly dark and fully light dither patterns at lum 0.0 and 1.0\n threshold = threshold * 0.99 + 0.005;\n \n \u002F\u002F the lower lum_scaled is, the fewer pixels will be below the dither threshold, and thus will use the lower bound colour,\n \u002F\u002F and vice-versa\n float ramp_val = lum_scaled \u003C threshold ? 0.0f : 1.0f;\n \u002F\u002F sample at the lower bound colour if ramp_val is 0.0, upper bound colour if 1.0\n float col_sample = mix(lum_lower, lum_upper, ramp_val);\n vec3 final_col = texture(tDiffuse, vec2(col_sample, 0.5)).rgb;\n \n \u002F\u002F return the final colour!\n gl_FragColor = vec4(final_col, 1.0);\n }\n `,\n}\n","id":"mod_TxMMXk7etZaKRLMi5L36yK","is_binary":false,"title":"DitherShader.js","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"rkKxSTjHjbt","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"SJMBpiBsWt"},{"code":"export const NoiseShader = {\n uniforms: {\n tDiffuse: { type: \"t\", value: null },\n amount: { value: 0.08 },\n speed: { value: 0.5 },\n time: { value: 0 },\n },\n vertexShader: \u002F*glsl*\u002F `\n varying vec2 vUv;\n\n\t\tvoid main() {\n\t\t\tvUv = uv;\n\t\t\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\t\t}\n `,\n fragmentShader: \u002F*glsl*\u002F `\n uniform sampler2D tDiffuse;\n\t\tuniform float amount;\n\t\tuniform float speed;\n\t\tuniform float time;\n\n\t\tvarying vec2 vUv;\n\n\n\t\tfloat random(vec2 n, float offset ){\n\t\t\treturn .5 - fract(sin(dot(n.xy + vec2( offset, 0. ), vec2(12.9898, 78.233)))* 43758.5453);\n\t\t}\n\n\t\tvoid main() {\n\t\t\tvec3 color = texture2D(tDiffuse, vUv).rgb;\n\t\t\tcolor += vec3( amount * random( vUv, .0001 * speed * time ) );\n\t\t\tgl_FragColor = vec4(color,1.0);\n\t\t}\n `,\n}\n","id":"mod_BD4qfYnNVQDkkois5e6qCL","is_binary":false,"title":"NoiseShader.js","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"H1qgS6iSsbt","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"SJMBpiBsWt"},{"code":"export const RGBShiftShader = {\n uniforms: {\n tDiffuse: { value: null },\n amount: { value: 0.02 },\n },\n\n vertexShader: \u002F*glsl*\u002F `\n varying vec2 vUv;\n\n\t\tvoid main() {\n\t\t\tvUv = uv;\n\t\t\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\t\t}\n `,\n fragmentShader: \u002F*glsl*\u002F `\n\t\tuniform sampler2D tDiffuse;\n\t\tuniform float amount;\n\n\t\tvarying vec2 vUv;\n\n\t\tvoid main() {\n\n\t\t\tvec2 offset = vec2( -0.1, 0.) * amount;\n\t\t\tvec4 cr = texture2D(tDiffuse, vUv + offset);\n\t\t\tvec4 cga = texture2D(tDiffuse, vUv);\n\t\t\tvec4 cb = texture2D(tDiffuse, vUv - offset);\n\t\t\tgl_FragColor = vec4(cr.r, cga.g, cb.b, cga.a);\n\t\t}\n `,\n}\n","id":"mod_sVGnyAWjc5JZZ2zXX2wYq","is_binary":false,"title":"RGBShiftShader.js","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"BJixrasBoWF","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"SJMBpiBsWt"},{"code":"https:\u002F\u002Frawcdn.githack.com\u002Fbfollington\u002Fuse-animation\u002F55eb38e591426d628c0ee6fe4a2494a3e42cedfe\u002Fsrc\u002Fresources\u002Fbayer2tile16.png","id":"mod_ExJFXdUf9TATX6oM2JYnEA","is_binary":true,"title":"bayer2tile16.png","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"SkcZSpjHoWF","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"HyHH6jSsbt"},{"code":"https:\u002F\u002Frawcdn.githack.com\u002Fbfollington\u002Fuse-animation\u002F55eb38e591426d628c0ee6fe4a2494a3e42cedfe\u002Fsrc\u002Fresources\u002Fpalette_mono.png","id":"mod_Pn6p2PFc31LmbrN2f6rwYM","is_binary":true,"title":"palette_mono.png","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"SkoZH6sSsWK","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"HyHH6jSsbt"},{"code":"https:\u002F\u002Frawcdn.githack.com\u002Fbfollington\u002Fuse-animation\u002F55eb38e591426d628c0ee6fe4a2494a3e42cedfe\u002Fsrc\u002Fresources\u002Fpalette_eeve.png","id":"mod_GALffDWp6UsjWPBLvpqgWw","is_binary":true,"title":"palette_eeve.png","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"Hyn-BajHjbt","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"HyHH6jSsbt"},{"code":"https:\u002F\u002Frawcdn.githack.com\u002Fbfollington\u002Fuse-animation\u002F55eb38e591426d628c0ee6fe4a2494a3e42cedfe\u002Fsrc\u002Fresources\u002Fbayer4tile8.png","id":"mod_EzfBN8qAUFZFrXpowMt2Ef","is_binary":true,"title":"bayer4tile8.png","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"HyTWHaoSi-F","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"HyHH6jSsbt"},{"code":"https:\u002F\u002Frawcdn.githack.com\u002Fbfollington\u002Fuse-animation\u002F55eb38e591426d628c0ee6fe4a2494a3e42cedfe\u002Fsrc\u002Fresources\u002Fpalette_hollow.png","id":"mod_7QRL9e47o8T3d7yZUCXvAT","is_binary":true,"title":"palette_hollow.png","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"SkC-rTiBsbK","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"HyHH6jSsbt"},{"code":"\u003C!DOCTYPE html\u003E\n\u003Chtml lang=\"en\"\u003E\n\n\u003Chead\u003E\n\t\u003Cmeta charset=\"utf-8\"\u003E\n\t\u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\"\u003E\n\t\u003Cmeta name=\"theme-color\" content=\"#000000\"\u003E\n\t\u003C!--\n manifest.json provides metadata used when your web app is added to the\n homescreen on Android. See https:\u002F\u002Fdevelopers.google.com\u002Fweb\u002Ffundamentals\u002Fengage-and-retain\u002Fweb-app-manifest\u002F\n --\u003E\n\t\u003Clink rel=\"manifest\" href=\"%PUBLIC_URL%\u002Fmanifest.json\"\u003E\n\t\u003Clink rel=\"shortcut icon\" href=\"%PUBLIC_URL%\u002Ffavicon.ico\"\u003E\n\t\u003C!--\n Notice the use of %PUBLIC_URL% in the tags above.\n It will be replaced with the URL of the `public` folder during the build.\n Only files inside the `public` folder can be referenced from the HTML.\n\n Unlike \"\u002Ffavicon.ico\" or \"favicon.ico\", \"%PUBLIC_URL%\u002Ffavicon.ico\" will\n work correctly both with client-side routing and a non-root public URL.\n Learn how to configure a non-root public URL by running `npm run build`.\n --\u003E\n\t\u003Ctitle\u003EReact App\u003C\u002Ftitle\u003E\n\u003C\u002Fhead\u003E\n\n\u003Cbody\u003E\n\t\u003Cnoscript\u003E\n\t\tYou need to enable JavaScript to run this app.\n\t\u003C\u002Fnoscript\u003E\n\t\u003Cdiv id=\"root\"\u003E\u003C\u002Fdiv\u003E\n\t\u003C!--\n This HTML file is a template.\n If you open it directly in the browser, you will see an empty page.\n\n You can add webfonts, meta tags, or analytics to this file.\n The build step will place the bundled scripts into the \u003Cbody\u003E tag.\n\n To begin the development, run `npm start` or `yarn start`.\n To create a production bundle, use `npm run build` or `yarn build`.\n --\u003E\n\u003C\u002Fbody\u003E\n\n\u003C\u002Fhtml\u003E","id":"mod_4SR936T71UkxY31puEvMGw","is_binary":false,"title":"index.html","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"SJ3rTiBsWY","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"S1rasroZY"},{"code":"{\n \"infiniteLoopProtection\": false,\n \"hardReloadOnChange\": false,\n \"view\": \"browser\"\n}\n","id":"mod_Ax5xbhf8WqvmMM1uoAart7","is_binary":false,"title":"sandbox.config.json","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"rJ6S6oHsbF","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":null},{"code":"{\n \"compilerOptions\": {\n \"target\": \"es5\",\n \"lib\": [\n \"dom\",\n \"dom.iterable\",\n \"esnext\"\n ],\n \"allowJs\": true,\n \"skipLibCheck\": true,\n \"esModuleInterop\": true,\n \"allowSyntheticDefaultImports\": true,\n \"strict\": true,\n \"forceConsistentCasingInFileNames\": true,\n \"noFallthroughCasesInSwitch\": true,\n \"module\": \"esnext\",\n \"moduleResolution\": \"node\",\n \"resolveJsonModule\": true,\n \"isolatedModules\": true,\n \"noEmit\": true,\n \"jsx\": \"react-jsx\"\n },\n \"include\": [\n \"src\"\n ]\n}\n","id":"mod_PyjBWo7YBSBagfdhEgpHtz","is_binary":false,"title":"tsconfig.json","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"H1PbrpsSj-K","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":null},{"code":"https:\u002F\u002Frawcdn.githack.com\u002Fbfollington\u002Fuse-animation\u002F55eb38e591426d628c0ee6fe4a2494a3e42cedfe\u002Fsrc\u002Fresources\u002Fbayer16tile2.png","id":"mod_DxXvnf4Y8WF6NSyuusEsBg","is_binary":true,"title":"bayer16tile2.png","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"rk_-rTsSiWt","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"HyHH6jSsbt"},{"code":"https:\u002F\u002Frawcdn.githack.com\u002Fbfollington\u002Fuse-animation\u002F55eb38e591426d628c0ee6fe4a2494a3e42cedfe\u002Fsrc\u002Fresources\u002Fbayer8tile4.png","id":"mod_Aokxh1UmTzuDv8yqtk66WJ","is_binary":true,"title":"bayer8tile4.png","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"rkK-S6sHsWt","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"HyHH6jSsbt"},{"code":"import React, { Suspense } from \"react\"\nimport { Canvas } from \"@react-three\u002Ffiber\"\nimport { Sky, OrbitControls, Html } from \"@react-three\u002Fdrei\"\nimport Wall from \".\u002FWall\"\nimport Ribbon from \".\u002FRibbon\"\nimport Effects from \".\u002FEffects\"\n\nexport default function App() {\n return (\n \u003CCanvas camera={{ position: [15, 15, 15] }}\u003E\n \u003Ccolor attach=\"background\" args={[\"black\"]} \u002F\u003E\n {\u002F* \u003CSky azimuth={1} inclination={0.1} distance={1000} \u002F\u003E *\u002F}\n \u003CambientLight intensity={0.1} \u002F\u003E\n \u003CpointLight position={[10, 10, 10]} \u002F\u003E\n \u003CSuspense fallback={null}\u003E\n {\u002F* \u003CGrass \u002F\u003E *\u002F}\n \u003CpointLight position={[30, 0, 0]} color=\"blue\" intensity={10} \u002F\u003E\n {\u002F* \u003CWall position={[-30, 10, -20]} color=\"blue\" scale={[1, 2, 3]} \u002F\u003E *\u002F}\n \u003CWall position={[10, 0, 0]} scale={[1, 1, 1.5]} \u002F\u003E\n \u003CWall position={[0, 0, 0]} scale={[1, 2.5, 1]} \u002F\u003E\n \u003CWall position={[-10, 0, 0]} scale={[1, 2, 1]} \u002F\u003E\n \u003CWall position={[-30, 30, 0]} scale={[1, 1, 2]} \u002F\u003E\n \u003CWall position={[-30, -10, -10]} scale={[2, 1, 3]} \u002F\u003E\n\n {\u002F* \u003CRibbon id={1} color=\"#7b505c\" \u002F\u003E\n \u003CRibbon id={64} color=\"#9b9880\" \u002F\u003E\n \u003CRibbon id={128} color=\"#e4d6cf\" \u002F\u003E *\u002F}\n {\u002F* \u003CHtml\u003E\n \u003Cp\n style={{\n margin: 0,\n align: \"center\",\n transform: \"translate(-48%, -50%)\",\n color: \"white\",\n letterSpacing: \"54px\",\n fontFamily: \"Space Age\",\n fontSize: \"256px\",\n pointerEvents: \"none\",\n }}\u003E\n ☻\n \u003C\u002Fp\u003E\n \u003C\u002FHtml\u003E *\u002F}\n \u003CEffects \u002F\u003E\n \u003C\u002FSuspense\u003E\n \u003COrbitControls minPolarAngle={Math.PI \u002F 10} maxPolarAngle={Math.PI \u002F 1.5} \u002F\u003E\n \u003C\u002FCanvas\u003E\n )\n}\n","id":"mod_HfyvKWem4tYcKCeanUtrSY","is_binary":false,"title":"App.js","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"SJCBporsbK","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"HyxSToBs-F"},{"code":"import { useRef } from \"react\"\nimport { interpolator, useAnimation, useObservable } from \".\u002FuseAnimation\u002FuseAnimation\"\nimport KEYS from \".\u002FuseInput\u002Fkeys\"\nimport { useKeyDown } from \".\u002FuseInput\u002FkeyStream\"\nimport { useMouseMoveNormalised } from \".\u002FuseInput\u002FmouseStream\"\n\nconst IndependentAnimator = () =\u003E {\n const target = useRef\u003CHTMLDivElement\u003E(null)\n\n const scale = useObservable(1)\n useAnimation(scale, interpolator(0, 1, \"easeInOutCubic\"), 250, (v) =\u003E {\n if (target.current) {\n target.current.style.transformOrigin = \"center\"\n target.current.style.transform = `scale(${v})`\n }\n })\n\n const opacity = useObservable(1)\n useAnimation(opacity, interpolator(0, 1, \"linear\"), 10, (v) =\u003E {\n if (target.current) {\n target.current.style.opacity = `${v}`\n }\n })\n\n useKeyDown(KEYS.up_arrow, () =\u003E {\n scale.swap((s) =\u003E s + 2)\n })\n\n useKeyDown(KEYS.down_arrow, () =\u003E {\n scale.swap((s) =\u003E s - 2)\n })\n\n return \u003Cdiv ref={target} style={{ width: \"32px\", height: \"32px\", backgroundColor: \"red\" }} \u002F\u003E\n}\n\nconst DomAnimation = () =\u003E {\n const items = [...Array(500)].map((_, i) =\u003E i)\n\n return (\n \u003Cdiv style={{ display: \"flex\", alignItems: \"center\", flexWrap: \"wrap\", height: \"100%\", justifyContent: \"center\" }}\u003E\n {items.map((i) =\u003E (\n \u003CIndependentAnimator key={i} \u002F\u003E\n ))}\n \u003C\u002Fdiv\u003E\n )\n}\n\nexport default DomAnimation\n","id":"mod_WrfyKXw4EosSufM1AeAfmw","is_binary":false,"title":"DomAnimation.tsx","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"HkJlBTsSoZY","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"HyxSToBs-F"},{"code":"import { motion, useMotionValue, useSpring } from \"framer-motion\"\nimport { useRef } from \"react\"\nimport { interpolator, useAnimation, useObservable } from \".\u002FuseAnimation\u002FuseAnimation\"\nimport KEYS from \".\u002FuseInput\u002Fkeys\"\nimport { useKeyDown } from \".\u002FuseInput\u002FkeyStream\"\nimport { useMouseMoveNormalised } from \".\u002FuseInput\u002FmouseStream\"\n\nconst IndependentAnimator = () =\u003E {\n const scale = useSpring(1)\n\n useKeyDown(KEYS.up_arrow, () =\u003E {\n scale.set(scale.get() + 2)\n })\n\n useKeyDown(KEYS.down_arrow, () =\u003E {\n scale.set(scale.get() - 2)\n })\n\n return \u003Cmotion.div transition={{ duration: 200 }} style={{ scale, width: \"32px\", height: \"32px\", backgroundColor: \"red\" }} \u002F\u003E\n}\n\nconst DomAnimationFramer = () =\u003E {\n const items = [...Array(500)].map((_, i) =\u003E i)\n\n return (\n \u003Cdiv style={{ display: \"flex\", alignItems: \"center\", flexWrap: \"wrap\", height: \"100%\", justifyContent: \"center\" }}\u003E\n {items.map((i) =\u003E (\n \u003CIndependentAnimator key={i} \u002F\u003E\n ))}\n \u003C\u002Fdiv\u003E\n )\n}\n\nexport default DomAnimationFramer\n","id":"mod_TydQMdWocjv77TQrzmfZW7","is_binary":false,"title":"DomAnimationFramer.tsx","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"SyxlrTsrobY","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"HyxSToBs-F"},{"code":"import * as THREE from \"three\"\nimport React, { useRef, useMemo, useEffect } from \"react\"\nimport { extend, useThree, useFrame, useLoader } from \"@react-three\u002Ffiber\"\nimport { EffectComposer } from \"three\u002Fexamples\u002Fjsm\u002Fpostprocessing\u002FEffectComposer\"\nimport { ShaderPass } from \"three\u002Fexamples\u002Fjsm\u002Fpostprocessing\u002FShaderPass\"\nimport { RenderPass } from \"three\u002Fexamples\u002Fjsm\u002Fpostprocessing\u002FRenderPass\"\nimport { AfterimagePass } from \"three\u002Fexamples\u002Fjsm\u002Fpostprocessing\u002FAfterimagePass\"\nimport { GlitchPass } from \"three\u002Fexamples\u002Fjsm\u002Fpostprocessing\u002FGlitchPass\"\nimport { UnrealBloomPass } from \"three\u002Fexamples\u002Fjsm\u002Fpostprocessing\u002FUnrealBloomPass\"\nimport { CrtShader } from \".\u002Fshaders\u002FCrtShader\"\nimport { TiltShiftShader } from \".\u002Fshaders\u002FTiltShiftShader\"\nimport { NoiseShader } from \".\u002Fshaders\u002FNoiseShader\"\nimport { DitherShader } from \".\u002Fshaders\u002FDitherShader\"\nimport { SpaceShader } from \".\u002Fshaders\u002FSpaceShader\"\nimport { RGBShiftShader } from \".\u002Fshaders\u002FRGBShiftShader\"\n\nimport palette from \".\u002Fresources\u002Fpalette_twilight.png\"\nimport dither from \".\u002Fresources\u002Fbayer16tile2.png\"\n\nextend({\n EffectComposer,\n ShaderPass,\n RenderPass,\n UnrealBloomPass,\n AfterimagePass,\n GlitchPass,\n})\n\nexport default function Effects() {\n const composer = useRef()\n const { scene, gl, size, camera } = useThree()\n const aspect = useMemo(() =\u003E new THREE.Vector2(512, 512), [])\n const noisePass = useRef()\n const spacePass = useRef()\n const ditherPass = useRef()\n\n const [paletteTex, ditherTex] = useLoader(THREE.TextureLoader, [palette, dither])\n ditherTex.wrapS = ditherTex.wrapT = THREE.RepeatWrapping\n console.log(paletteTex.image.width, paletteTex.image.height)\n console.log(ditherTex.image.width, ditherTex.image.height)\n\n useEffect(() =\u003E void composer.current.setSize(size.width, size.height), [size])\n useFrame(({ clock }) =\u003E {\n \u002F\u002F noisePass.current.uniforms.time.value = clock.elapsedTime\n \u002F\u002F spacePass.current.uniforms.time.value = clock.elapsedTime\n ditherPass.current.uniforms.tDither.value = ditherTex\n ditherPass.current.uniforms.tPalette.value = paletteTex\n composer.current.render()\n }, 1)\n\n return (\n \u003CeffectComposer ref={composer} args={[gl]}\u003E\n \u003CrenderPass attachArray=\"passes\" scene={scene} camera={camera} \u002F\u003E\n {\u002F* \u003CafterimagePass attachArray=\"passes\" args={[0.98]} scene={scene} camera={camera} \u002F\u003E *\u002F}\n\n {\u002F* \u003CglitchPass\n attachArray=\"passes\"\n args={[0.1]}\n scene={scene}\n camera={camera}\n \u002F\u003E *\u002F}\n\n {\u002F* \u003CshaderPass attachArray=\"passes\" args={[TiltShiftShader]} scene={scene} camera={camera} \u002F\u003E *\u002F}\n {\u002F* \u003CshaderPass ref={noisePass} attachArray=\"passes\" args={[NoiseShader]} scene={scene} camera={camera} \u002F\u003E *\u002F}\n {\u002F* \u003CshaderPass ref={spacePass} attachArray=\"passes\" args={[SpaceShader]} scene={scene} camera={camera} \u002F\u003E *\u002F}\n {\u002F* \u003CshaderPass attachArray=\"passes\" args={[RGBShiftShader]} scene={scene} camera={camera} \u002F\u003E *\u002F}\n {\u002F* \u003CunrealBloomPass attachArray=\"passes\" args={[aspect, 0.4, 0.5, 0]} \u002F\u003E *\u002F}\n \u003CshaderPass ref={ditherPass} attachArray=\"passes\" args={[DitherShader]} scene={scene} camera={camera} \u002F\u003E\n\n {\u002F* \u003CshaderPass attachArray=\"passes\" args={[CrtShader]} scene={scene} camera={camera} \u002F\u003E *\u002F}\n \u003C\u002FeffectComposer\u003E\n )\n}\n\n\u002F\u002F const blendPass = new THREE.ShaderPass(THREE.BlendShader, \"tDiffuse1\");\n\u002F\u002F blendPass.uniforms[\"tDiffuse2\"].value = savePass.renderTarget.texture;\n\u002F\u002F blendPass.uniforms[\"mixRatio\"].value = 0.8;\n","id":"mod_K4M2PZNiuH4xjpbjvJbFAU","is_binary":false,"title":"Effects.js","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"SkZgHairiWK","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"HyxSToBs-F"},{"code":"import * as THREE from \"three\"\nimport React, { useRef, useMemo, useEffect, useState } from \"react\"\nimport SimplexNoise from \"simplex-noise\"\nimport { useFrame, useLoader } from \"@react-three\u002Ffiber\"\nimport bg from \".\u002Fresources\u002Fseamless8.png\"\nimport \".\u002Fmaterials\u002FShinyMaterial\"\nimport { Html } from \"@react-three\u002Fdrei\"\n\nvar RIBBON_LEN = 100 \u002F\u002Fnumber of spine point\n\nfunction createRibbonGeom() {\n \u002F\u002Fmake geometry, faces & colors for a ribbon\n var i\n\n const positions = []\n const normals = []\n const uvs = []\n const vertexColors = []\n\n \u002F\u002Fcreate verts + colors\n for (i = 0; i \u003C RIBBON_LEN; i++) {\n positions.push(new THREE.Vector3(i, i, 0))\n positions.push(new THREE.Vector3(i, i + 10, 0))\n vertexColors.push(new THREE.Color())\n vertexColors.push(new THREE.Color())\n }\n\n const indices = []\n\n \u002F\u002Fcreate faces\n for (i = 0; i \u003C RIBBON_LEN - 1; i++) {\n \u002F\u002F geom.faces.push(new THREE.Face3(i * 2, i * 2 + 1, i * 2 + 2))\n \u002F\u002F geom.faces.push(new THREE.Face3(i * 2 + 1, i * 2 + 3, i * 2 + 2))\n\n indices.push(i * 2)\n indices.push(i * 2 + 1)\n indices.push(i * 2 + 2)\n\n indices.push(i * 2 + 1)\n indices.push(i * 2 + 3)\n indices.push(i * 2 + 2)\n }\n\n return { positions, indices }\n}\n\nfunction lerp(v0, v1, t) {\n return v0 * (1 - t) + v1 * t\n}\n\nconst noise = new SimplexNoise()\n\nfunction getNoiseAngle(time, noiseId, zOffset) {\n \u002F\u002F return noise.noise3d( noiseTime, noiseId, zOffset ) * Math.PI*2;\n \u002F\u002F console.log(time, noiseId, zOffset)\n return noise.noise3D(time, noiseId, zOffset) * Math.PI * 2\n \u002F\u002F return Math.PI * 2 * (Math.sin(time) + 1)\n}\n\nexport default function Ribbon({ id = 1, color = \"red\" }) {\n const material = useRef()\n const mesh = useRef()\n const [texture] = useLoader(THREE.TextureLoader, [bg])\n texture.wrapS = texture.wrapT = THREE.RepeatWrapping\n\n const up = new THREE.Vector3(1, 0, 0)\n const direction = useRef(new THREE.Vector3(0, 0, 0))\n const normal = useRef(new THREE.Vector3(0, 0, 0))\n\n const arm1 = useRef(new THREE.Vector3())\n const arm2 = useRef(new THREE.Vector3())\n const arm3 = useRef(new THREE.Vector3())\n\n const arm1T = useRef(new THREE.Vector3())\n const arm2T = useRef(new THREE.Vector3())\n const arm3T = useRef(new THREE.Vector3())\n\n const xAxis = new THREE.Vector3(1, 0, 0)\n const yAxis = new THREE.Vector3(0, 1, 0)\n const zAxis = new THREE.Vector3(0, 0, 1)\n\n const noiseId = useRef(-1)\n const ribbonWidth = useRef(-1)\n\n const head = useRef(new THREE.Vector3(0, 0, 0))\n const prev = useRef(new THREE.Vector3(0, 0, 0))\n\n const geometry = useRef()\n\n const [debug, setDebug] = useState(\"\")\n\n const onInit = () =\u003E {\n noiseId.current = id \u002F 300\n\n \u002F\u002F generate thiccness\n ribbonWidth.current = Math.random() * 1.5 + 0.25\n if (Math.random() \u003C 0.2) {\n ribbonWidth.current = 4\n }\n\n \u002F\u002Fhead is the thing that moves, prev follows behind\n head.current = new THREE.Vector3(0, 0, 0)\n prev.current = new THREE.Vector3(0, 0, 0)\n\n \u002F\u002F construct mesh\n const geo = createRibbonGeom()\n geometry.current.setFromPoints(geo.positions)\n geometry.current.setIndex(geo.indices)\n geometry.current.computeVertexNormals()\n\n \u002F\u002Fmovement arm vectors\n var armLenFac = 1.7\n arm1.current = new THREE.Vector3(8 * armLenFac, 0, 0)\n arm2.current = new THREE.Vector3(4 * armLenFac, 0, 0)\n arm3.current = new THREE.Vector3(1.5 * armLenFac, 0, 0)\n\n onReset()\n }\n\n const onReset = () =\u003E {\n var i\n\n \u002F\u002Freset prev position\n prev.current.copy(head.current)\n\n \u002F\u002Freset mesh geom\n for (i = 0; i \u003C RIBBON_LEN; i++) {\n geometry.current.attributes.position.array[i * 6] = head.current.x\n geometry.current.attributes.position.array[i * 6 + 1] = head.current.y\n geometry.current.attributes.position.array[i * 6 + 2] = head.current.z\n geometry.current.attributes.position.array[i * 6 + 3] = head.current.x\n geometry.current.attributes.position.array[i * 6 + 4] = head.current.y\n geometry.current.attributes.position.array[i * 6 + 5] = head.current.z\n }\n\n \u002F\u002Finit colors for this ribbon\n \u002F\u002Fhue is set by noiseId\n let hue1 = (noiseId + Math.random() * 0.01) % 2\n let hue2 = (noiseId + Math.random() * 0.01) % 2\n\n if (Math.random() \u003C 0.1) {\n hue1 = Math.random()\n }\n if (Math.random() \u003C 0.1) {\n hue2 = Math.random()\n }\n\n var sat = Math.random() * 0.4 + 0.6\n var lightness = Math.random() * 0.4 + 0.2\n\n var col = new THREE.Color()\n\n \u002F\u002F TODO: fix this so the ribbons change colour along their length\n for (i = 0; i \u003C RIBBON_LEN - 1; i++) {\n \u002F\u002Fadd lightness gradient based on spine position\n col.setHSL(lerp(hue1, hue2, i \u002F RIBBON_LEN), sat, lightness)\n\n \u002F\u002F this.meshGeom.faces[i*2].color.copy(col);\n \u002F\u002F this.meshGeom.faces[i*2+1].color.copy(col);\n }\n\n geometry.current.verticesNeedUpdate = true\n geometry.current.colorsNeedUpdate = true\n }\n\n useFrame(({ clock }, delta) =\u003E {\n \u002F\u002F material.current.time += delta + Math.sin(clock.elapsedTime \u002F 10) \u002F 1000\n mesh.current.rotation.x = Math.sin(clock.elapsedTime) * (Math.PI \u002F 20)\n mesh.current.rotation.y = Math.cos(clock.elapsedTime) * (Math.PI \u002F 20)\n mesh.current.scale.y = 0.9 + 0.1 * Math.sin(clock.elapsedTime)\n mesh.current.scale.x = 0.9 + 0.1 * Math.cos(clock.elapsedTime)\n\n prev.current.copy(head.current)\n\n \u002F\u002Fmove arms\n arm1T.current.copy(arm1.current)\n arm2T.current.copy(arm2.current)\n arm3T.current.copy(arm3.current)\n\n const t = clock.elapsedTime \u002F 5.0\n\n arm1T.current.applyAxisAngle(zAxis, getNoiseAngle(t, noiseId.current, 0))\n arm1T.current.applyAxisAngle(yAxis, getNoiseAngle(t, noiseId.current, 20))\n\n arm2T.current.applyAxisAngle(zAxis, getNoiseAngle(t, noiseId.current, 50))\n arm2T.current.applyAxisAngle(xAxis, getNoiseAngle(t, noiseId.current, 70))\n\n arm3T.current.applyAxisAngle(xAxis, getNoiseAngle(t, noiseId.current, 100))\n arm3T.current.applyAxisAngle(yAxis, getNoiseAngle(t, noiseId.current, 150))\n\n \u002F\u002FMOVE HEAD\n head.current.copy(arm1T.current).add(arm2T.current).add(arm3T.current)\n\n \u002F\u002Fcalc new L + R edge positions from direction between head and prev\n direction.current.subVectors(head.current, prev.current).normalize()\n normal.current.crossVectors(direction.current, up).normalize()\n normal.current.multiplyScalar(ribbonWidth.current)\n\n \u002F\u002Fshift each 2 verts down one posn\n \u002F\u002Fe.g. copy verts (0,1) -\u003E (2,3)\n const verts = geometry.current.attributes.position.array\n for (var i = RIBBON_LEN - 1; i \u003E 0; i--) {\n verts[i * 6] = verts[(i - 1) * 6]\n verts[i * 6 + 1] = verts[(i - 1) * 6 + 1]\n verts[i * 6 + 2] = verts[(i - 1) * 6 + 2]\n verts[i * 6 + 3] = verts[(i - 1) * 6 + 3]\n verts[i * 6 + 4] = verts[(i - 1) * 6 + 4]\n verts[i * 6 + 5] = verts[(i - 1) * 6 + 5]\n }\n\n \u002F\u002Fpopulate 1st 2 verts with left and right normalHelper\n const v0 = new THREE.Vector3().copy(head.current).add(normal.current)\n verts[0] = v0.x\n verts[1] = v0.y\n verts[2] = v0.z\n const v1 = new THREE.Vector3().copy(head.current).sub(normal.current)\n verts[3] = v1.x\n verts[4] = v1.y\n verts[5] = v1.z\n\n geometry.current.attributes.position.needsUpdate = true\n geometry.current.computeFaceNormals()\n geometry.current.computeVertexNormals()\n\n \u002F\u002F setDebug(`${head.current.x}, ${head.current.y}, ${head.current.z}`)\n })\n\n useEffect(() =\u003E {\n onInit()\n }, [onInit])\n\n return (\n \u003C\u003E\n \u003Cmesh position={[0, 0, 0]} ref={mesh}\u003E\n {\u002F* \u003CshinyMaterial ref={material} attach=\"material\" noiseTexture={texture} \u002F\u003E *\u002F}\n \u003CmeshStandardMaterial attach=\"material\" color={color} side={THREE.DoubleSide} \u002F\u003E\n \u003CbufferGeometry attach=\"geometry\" ref={geometry} \u002F\u003E\n {\u002F* \u003CmeshPhongMaterial\n attach=\"material\"\n side={THREE.DoubleSide}\n vertexColors={THREE.FaceColors}\n color={\"#FFFFFF\"}\n shininess={30}\n specular={0x50473b}\n frustumCulled={false}\n \u002F\u003E *\u002F}\n \u003C\u002Fmesh\u003E\n {\u002F* \u003CHtml\u003E\n \u003Cp\u003E{debug}\u003C\u002Fp\u003E\n \u003C\u002FHtml\u003E *\u002F}\n \u003C\u002F\u003E\n )\n}\n","id":"mod_2U771sGqvGQ6MdmtaTsNCA","is_binary":false,"title":"Ribbon.js","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"HyfxSTiSo-K","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"HyxSToBs-F"},{"code":"\u002F\u002F Based on https:\u002F\u002Fcodepen.io\u002Fal-ro\u002Fpen\u002FjJJygQ by al-ro, but rewritten in react-three-fiber\nimport { useLoader } from \"@react-three\u002Ffiber\"\nimport React, { useCallback, useEffect, useRef } from \"react\"\nimport { interval } from \"rxjs\"\nimport { map } from \"rxjs\u002Foperators\"\nimport * as THREE from \"three\"\nimport \".\u002Fmaterials\u002FShinyMaterial\"\nimport bg from \".\u002Fresources\u002Fseamless8.png\"\nimport { useAnimation } from \".\u002FuseAnimation\u002Fthree\"\nimport { interpolator, sequence, useObservable } from \".\u002FuseAnimation\u002FuseAnimation\"\nimport KEYS from \".\u002FuseInput\u002Fkeys\"\nimport { useKeyDown, useKeyHeld } from \".\u002FuseInput\u002FkeyStream\"\nimport { useMouseMoveNormalised } from \".\u002FuseInput\u002FmouseStream\"\n\nexport default function Wall(props: any) {\n const material = useRef()\n const mesh = useRef()\n const [texture] = useLoader(THREE.TextureLoader, [bg])\n texture.wrapS = texture.wrapT = THREE.RepeatWrapping\n\n const anim = sequence(interpolator(0, 1.2, \"easeOutCubic\"), interpolator(1.2, 1, \"easeOutCubic\"))\n\n const angle = useObservable(0)\n useAnimation(angle, interpolator(0, 1, \"easeOutQuad\"), 250, (v) =\u003E {\n const m = mesh.current as any\n m.rotation.x = v\n m.rotation.y = v\n })\n\n const xscale = useObservable(1)\n useAnimation(xscale, anim, 500, (v) =\u003E {\n const m = mesh.current as any\n m.scale.z = v\n })\n\n const yscale = useObservable(1)\n useAnimation(\n yscale,\n anim,\n 500,\n useCallback((v) =\u003E {\n const m = mesh.current as any\n m.scale.y = v\n }, []),\n )\n\n useKeyDown(KEYS.up_arrow, () =\u003E {\n yscale.swap((s) =\u003E s + 0.1)\n })\n\n useKeyDown(KEYS.down_arrow, () =\u003E {\n yscale.swap((s) =\u003E s - 0.1)\n })\n\n useKeyHeld(KEYS.left_arrow, 50, () =\u003E {\n angle.swap((s) =\u003E s - 0.05)\n })\n\n useKeyHeld(KEYS.right_arrow, 50, () =\u003E {\n angle.swap((s) =\u003E s + 0.05)\n })\n\n useMouseMoveNormalised(([x, y]) =\u003E {\n yscale.set(y * 4 + 0.5)\n xscale.set(x * 4 + 0.5)\n }, 50)\n\n \u002F\u002F useEffect(() =\u003E {\n \u002F\u002F const s = combineLatest([\n \u002F\u002F mousemovenormalised$.pipe(\n \u002F\u002F sampleTime(100),\n \u002F\u002F map(([x]) =\u003E Math.PI * 2 * Math.sin(x * Math.PI)),\n \u002F\u002F ),\n \u002F\u002F interval(100),\n \u002F\u002F ])\n \u002F\u002F .pipe(map(([angle, time]) =\u003E angle + time \u002F 5.0))\n \u002F\u002F .subscribe(angle.set)\n \u002F\u002F return () =\u003E s.unsubscribe()\n \u002F\u002F }, [angle])\n\n useEffect(() =\u003E {\n const s = interval(500)\n .pipe(map((t) =\u003E Math.random() * Math.PI * 2))\n .subscribe(angle.set)\n return () =\u003E s.unsubscribe()\n }, [angle])\n\n return (\n \u003Cgroup {...props}\u003E\n \u003Cmesh ref={mesh}\u003E\n {\u002F* \u003CtorusKnotGeometry args={[1, 1, 50]} \u002F\u003E *\u002F}\n {\u002F* \u003CicosahedronGeometry args={[3, 10]} \u002F\u003E *\u002F}\n \u003CboxGeometry args={[4, 4]} \u002F\u003E\n {\u002F* \u003CshinyMaterial ref={material} noiseTexture={texture} \u002F\u003E *\u002F}\n {\u002F* \u003CmeshLambertMaterial color=\"red\" \u002F\u003E *\u002F}\n \u003CmeshPhongMaterial color={props.color || \"white\"} \u002F\u003E\n \u003C\u002Fmesh\u003E\n \u003C\u002Fgroup\u003E\n )\n}\n","id":"mod_bxiB5RedEN7DmgUAU7T4p","is_binary":false,"title":"Wall.tsx","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"r1meSasHs-Y","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"HyxSToBs-F"},{"code":"export const DIMENSIONS = {\n width: window.innerWidth,\n height: window.innerHeight,\n}\n","id":"mod_ByyCfp8ZwkbPJXySCgKS6S","is_binary":false,"title":"const.js","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"ByNlB6orobF","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"HyxSToBs-F"},{"code":"import React from \"react\"\nimport ReactDOM from \"react-dom\"\nimport App from \".\u002FApp\"\nimport DomAnimation from \".\u002FDomAnimation\"\nimport \".\u002Fstyles.css\"\n\nReactDOM.render(\n \u003CReact.StrictMode\u003E\n \u003CApp \u002F\u003E\n \u003C\u002FReact.StrictMode\u003E,\n document.getElementById(\"root\"),\n)\n","id":"mod_HpeX8fHSF2vziP6R34mLqM","is_binary":false,"title":"index.js","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"HyBeBajBibt","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"HyxSToBs-F"},{"code":"export const SpaceShader = {\n uniforms: {\n tDiffuse: { value: null },\n time: { value: 0 },\n opacity: { value: 0.15 },\n },\n vertexShader: \u002F*glsl*\u002F `\n varying vec2 vUv;\n\n\t\tvoid main() {\n\t\t\tvUv = uv;\n\t\t\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\t\t}\n `,\n fragmentShader: \u002F*glsl*\u002F `\n uniform sampler2D tDiffuse;\n\t\tuniform float time;\n\t\tuniform float opacity;\n\n\t\tvarying vec2 vUv;\n\n\t\t#define PI 3.14159265359\n\n\t\tmat2 rotate2d(float _angle){\n\t\t\treturn mat2(cos(_angle),-sin(_angle),\n\t\t\t\tsin(_angle),cos(_angle));\n\t\t}\n\n\t\tfloat hash( vec2 p )\n\t\t{\n\t\t\tfloat h = dot(p,vec2(127.1,311.7));\n\t\t\treturn -1.0 + 2.0*fract(sin(h)*43758.5453123);\n\t\t}\n\n\t\t\u002F\u002Fcheap value noise https:\u002F\u002Fwww.shadertoy.com\u002Fview\u002Flsf3WH\n\t\tfloat noise( in vec2 p )\n\t\t{\n\t\t\tvec2 i = floor( p );\n\t\t\tvec2 f = fract( p );\n\n\t\t\tvec2 u = f*f*(3.0-2.0*f);\n\n\t\t\treturn mix( mix( hash( i + vec2(0.0,0.0) ),\n\t\t\t hash( i + vec2(1.0,0.0) ), u.x),\n\t\t\tmix( hash( i + vec2(0.0,1.0) ),\n\t\t\t hash( i + vec2(1.0,1.0) ), u.x), u.y);\n\t\t}\n\n\t\tvoid main( )\n\t\t{\n\n\t\t\tvec2 uv = vUv;\n\t\t\tvec3 orig = texture2D(tDiffuse, uv).rgb;\n\n\t\t\t\u002F\u002Frotate uv space\n\t\t\tuv -= 0.5;\n\t\t\tuv = rotate2d( fract(time\u002F40.0)*2.0*PI ) * uv;\n\t\t\tuv += 0.5;\n\n\t\t\t\u002F\u002F4 corner grad blue\u002Fpink\n\t\t\tvec3 col1 = vec3(uv,1.0);\n\n\t\t\t\u002F\u002F4 corner grad pink\u002Forang\n\t\t\tvec3 col2 = vec3(1,uv);\n\n\t\t\t\u002F\u002Fmix over time\n\t\t\tfloat t = abs(2.0 * fract(time\u002F10.0) - 1.0);\n\t\t\tvec3 col = mix(col1,col2,t);\n\n\t\t\t\u002F\u002Fnoise clouds additive blend\n\t\t\t\u002F\u002Ff returns in -1 - 1 range'\n\t\t\t\u002F\u002Fsimple 1 octave\n\t\t\tfloat tn = time\u002F5.0;\n\t\t\tvec2 uvn = uv * 2.; \u002F\u002F noise scale\n\t\t\tfloat f = noise( uvn + tn);\n\t\t\tf *= 0.8;\n\t\t\tcol += f;\n\t\t\t\u002F\u002Fdarker\n\t\t\tcol -= 0.4;\n\n\t\t\tgl_FragColor = vec4(orig + col * opacity, 1.0);\n\t\t}\n `,\n}\n","id":"mod_5Qm3WNdvLdRThxxkob48PV","is_binary":false,"title":"SpaceShader.js","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"HkneBTjSibt","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"SJMBpiBsWt"},{"code":"export const TiltShiftShader = {\n uniforms: {\n tDiffuse: { type: \"t\", value: null },\n focusPos: { type: \"f\", value: 0.5 }, \u002F\u002F focus Y position 0 = bottom, 1 = top\n range: { type: \"f\", value: 0.5 }, \u002F\u002F distance to fade between none and full blur\n offset: { type: \"f\", value: 0.02 }, \u002F\u002F blur distance\n strength: { type: \"f\", value: 0.9 }, \u002F\u002F 0 = passthru, 1 = full\n },\n vertexShader: \u002F*glsl*\u002F `\n varying vec2 vUv;\n\n\t\tvoid main() {\n\t\t\tvUv = uv;\n\t\t\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\t\t}\n `,\n fragmentShader: \u002F*glsl*\u002F `\n uniform sampler2D tDiffuse;\n\t\tuniform float focusPos;\n\t\tuniform float offset;\n\t\tuniform float range;\n\t\tuniform float strength;\n\n\t\tvarying vec2 vUv;\n\n\t\tvoid main() {\n\n\t\t\tvec4 orig = texture2D(tDiffuse, vUv);\n\n\t\t\tvec2 uv = vUv;\n\n\t\t\tfloat tiltAmt = 1.0 - smoothstep(focusPos - range,focusPos,uv.y) + smoothstep(focusPos,focusPos + range,uv.y);\n\n\t\t\t\u002F\u002Fblur image\n\t\t\tvec4 blurImg = vec4( 0.0 );\n\n\t\t\tfloat offset9 = offset * 0.9;\n\t\t\tfloat offset7 = offset * 0.7;\n\t\t\tfloat offset4 = offset * 0.4;\n\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( 0.0, 0.4 ) * offset );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( 0.15, 0.37 ) * offset );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( 0.29, 0.29 ) * offset );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( -0.37, 0.15 ) * offset );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( 0.40, 0.0 ) * offset );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( 0.37, -0.15 ) * offset );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( 0.29, -0.29 ) * offset );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( -0.15, -0.37 ) * offset );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( 0.0, -0.4 ) * offset );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( -0.15, 0.37 ) * offset );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( -0.29, 0.29 ) * offset );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( 0.37, 0.15 ) * offset );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( -0.4, 0.0 ) * offset );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( -0.37, -0.15 ) * offset );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( -0.29, -0.29 ) * offset );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( 0.15, -0.37 ) * offset );\n\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( 0.15, 0.37 ) * offset9 );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( -0.37, 0.15 ) * offset9 );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( 0.37, -0.15 ) * offset9 );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( -0.15, -0.37 ) * offset9 );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( -0.15, 0.37 ) * offset9 );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( 0.37, 0.15 ) * offset9 );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( -0.37, -0.15 ) * offset9 );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( 0.15, -0.37 ) * offset9 );\n\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( 0.29, 0.29 ) * offset7 );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( 0.40, 0.0 ) * offset7 );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( 0.29, -0.29 ) * offset7 );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( 0.0, -0.4 ) * offset7 );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( -0.29, 0.29 ) * offset7 );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( -0.4, 0.0 ) * offset7 );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( -0.29, -0.29 ) * offset7 );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( 0.0, 0.4 ) * offset7 );\n\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( 0.29, 0.29 ) * offset4 );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( 0.4, 0.0 ) * offset4 );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( 0.29, -0.29 ) * offset4 );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( 0.0, -0.4 ) * offset4 );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( -0.29, 0.29 ) * offset4 );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( -0.4, 0.0 ) * offset4 );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( -0.29, -0.29 ) * offset4 );\n\t\t\tblurImg += texture2D( tDiffuse, vUv.xy + vec2( 0.0, 0.4 ) * offset4 );\n\n\t\t\tblurImg = blurImg \u002F 41.0;\n\n\t\t\tgl_FragColor = mix(orig, blurImg, tiltAmt * strength);\n\n\t\t}\n `,\n}\n","id":"mod_XJbHjh2DQ35XghPpmVDDdT","is_binary":false,"title":"TiltShiftShader.js","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"H1peBajBjZt","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"SJMBpiBsWt"},{"code":"html,\nbody,\n#root {\n margin: 0;\n padding: 0;\n width: 100%;\n height: 100%;\n}\n","id":"mod_U4KmLCWhYD5j6HxiwvvLTE","is_binary":false,"title":"styles.css","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"BkClSajSjZY","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"HyxSToBs-F"},{"code":"\u002F*\n * Easing Functions - inspired from http:\u002F\u002Fgizma.com\u002Feasing\u002F\n * only considering the t value for the range [0, 1] =\u003E [0, 1]\n *\u002F\nconst EasingFunctions = {\n \u002F\u002F no easing, no acceleration\n linear: (t: number) =\u003E t,\n \u002F\u002F accelerating from zero velocity\n easeInQuad: (t: number) =\u003E t * t,\n \u002F\u002F decelerating to zero velocity\n easeOutQuad: (t: number) =\u003E t * (2 - t),\n \u002F\u002F acceleration until halfway, then deceleration\n easeInOutQuad: (t: number) =\u003E (t \u003C 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t),\n \u002F\u002F accelerating from zero velocity\n easeInCubic: (t: number) =\u003E t * t * t,\n \u002F\u002F decelerating to zero velocity\n easeOutCubic: (t: number) =\u003E --t * t * t + 1,\n \u002F\u002F acceleration until halfway, then deceleration\n easeInOutCubic: (t: number) =\u003E (t \u003C 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1),\n \u002F\u002F accelerating from zero velocity\n easeInQuart: (t: number) =\u003E t * t * t * t,\n \u002F\u002F decelerating to zero velocity\n easeOutQuart: (t: number) =\u003E 1 - --t * t * t * t,\n \u002F\u002F acceleration until halfway, then deceleration\n easeInOutQuart: (t: number) =\u003E (t \u003C 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t),\n \u002F\u002F accelerating from zero velocity\n easeInQuint: (t: number) =\u003E t * t * t * t * t,\n \u002F\u002F decelerating to zero velocity\n easeOutQuint: (t: number) =\u003E 1 + --t * t * t * t * t,\n \u002F\u002F acceleration until halfway, then deceleration\n easeInOutQuint: (t: number) =\u003E (t \u003C 0.5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t),\n}\n\ntype E = typeof EasingFunctions\nexport type EaseFn = keyof E\n\nexport default EasingFunctions\n","id":"mod_PRXi3Q7Q2DxRxo5zij3jPx","is_binary":false,"title":"ease.ts","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"HJybrpiroWY","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"H17r6oBjbF"},{"code":"import { useFrame, useThree } from \"@react-three\u002Ffiber\"\nimport { useMemo, useRef } from \"react\"\nimport { Observable, of, Subject } from \"rxjs\"\nimport { expand, filter, map, share } from \"rxjs\u002Foperators\"\n\n\u002F\u002F Yoinked from https:\u002F\u002Fwww.learnrxjs.io\u002Flearn-rxjs\u002Frecipes\u002Fgameloop\n\nexport interface IFrameData {\n frameStartTime: number\n deltaTime: number\n}\n\n\u002F**\n * clampTo30FPS(frame)\n *\n * @param frame - {IFrameData} the frame data to check if we need to clamp to max of\n * 30fps time.\n *\n * If we get sporadic LONG frames (browser was navigated away or some other reason the frame takes a while) we want to throttle that so we don't JUMP ahead in any deltaTime calculations too far.\n *\u002F\nexport const clampTo30FPS = (frame: IFrameData) =\u003E {\n if (frame.deltaTime \u003E 1 \u002F 30) {\n frame.deltaTime = 1 \u002F 30\n }\n return frame\n}\n\nexport const clampTo60FPS = (frame: IFrameData) =\u003E {\n if (frame.deltaTime \u003E 1 \u002F 60) {\n frame.deltaTime = 1 \u002F 60\n }\n return frame\n}\n\n\u002F**\n * This function returns an observable that will emit the next frame once the\n * browser has returned an animation frame step. Given the previous frame it calculates\n * the delta time, and we also clamp it to 30FPS in case we get long frames.\n *\u002F\nconst calculateStep: (prevFrame?: IFrameData) =\u003E Observable\u003CIFrameData\u003E = (prevFrame?: IFrameData) =\u003E {\n return new Observable((observer) =\u003E {\n requestAnimationFrame((frameStartTime) =\u003E {\n console.log(\"raf\")\n\n \u002F\u002F Millis to seconds\n const deltaTime = prevFrame ? (frameStartTime - prevFrame.frameStartTime) \u002F 1000 : 0\n observer.next({\n frameStartTime,\n deltaTime,\n })\n })\n })\n}\n\n\u002F\u002F This is our core stream of frames. We use expand to recursively call the\n\u002F\u002F `calculateStep` function above that will give us each new Frame based on the\n\u002F\u002F window.requestAnimationFrame calls. Expand emits the value of the called functions\n\u002F\u002F returned observable, as well as recursively calling the function with that same\n\u002F\u002F emitted value. This works perfectly for calculating our frame steps because each step\n\u002F\u002F needs to know the lastStepFrameTime to calculate the next. We also only want to request\n\u002F\u002F a new frame once the currently requested frame has returned.\nexport const frames$ = of(undefined).pipe(\n expand((val) =\u003E calculateStep(val)),\n \u002F\u002F Expand emits the first value provided to it, and in this\n \u002F\u002F case we just want to ignore the undefined input frame\n filter((frame) =\u003E frame !== undefined),\n map((frame: IFrameData) =\u003E frame.deltaTime),\n share(),\n)\n\nexport const useFrameStream = () =\u003E {\n const s = useRef\u003CSubject\u003Cnumber\u003E\u003E(new Subject\u003Cnumber\u003E())\n useFrame(({ clock }) =\u003E {\n s.current.next(clock.getDelta())\n })\n return s\n}\n","id":"mod_NkibhaxCbFSNoh4z12r2p","is_binary":false,"title":"frameStream.ts","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"SJxbSTsSjZF","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"H17r6oBjbF"},{"code":"import { EaseFn, default as easing } from \".\u002Fease\"\nimport { concat, merge, Observable, of, Subject } from \"rxjs\"\nimport { delay, flatMap, map, share, switchMap, take, takeUntil, takeWhile, tap } from \"rxjs\u002Foperators\"\nimport * as React from \"react\"\nimport { useFrameStream } from \".\u002FframeStream\"\nimport { Animatable, Interpolator, mapInterpolator, ObservableSource } from \".\u002FuseAnimation\"\n\nexport const useAnimation = (source: ObservableSource, interpolator: Interpolator, duration: number, sink: (v: Animatable) =\u003E void) =\u003E {\n const underlying = React.useRef(source.value())\n\n const frames$ = useFrameStream()\n\n React.useEffect(() =\u003E {\n sink(underlying.current)\n\n const sub = source.changes\n .pipe(\n switchMap((v) =\u003E {\n const baseTime = Date.now()\n\n return concat(\n frames$.current.pipe(\n share(),\n map((dt) =\u003E (Date.now() - baseTime) \u002F duration),\n takeWhile((t) =\u003E t \u003C 1),\n ),\n of(1),\n ).pipe(map(mapInterpolator(interpolator, underlying.current, v).sample))\n }),\n )\n .subscribe((v) =\u003E {\n underlying.current = v\n sink(v)\n })\n\n return () =\u003E {\n sub.unsubscribe()\n }\n }, [duration, source, sink, interpolator, frames$])\n}\n","id":"mod_VG2iKikuhWg6JeMBVyLut7","is_binary":false,"title":"three.ts","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"rkbWrTjBs-K","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"H17r6oBjbF"},{"code":"import { EaseFn, default as easing } from \".\u002Fease\"\nimport { concat, merge, Observable, of, Subject } from \"rxjs\"\nimport { delay, flatMap, map, share, switchMap, take, takeUntil, takeWhile, tap } from \"rxjs\u002Foperators\"\nimport * as React from \"react\"\nimport { frames$, useFrameStream } from \".\u002FframeStream\"\n\nexport function interpolator(from: number, to: number, easeFn: EaseFn) {\n const e: (t: number) =\u003E number = easing[easeFn]\n return {\n end: to,\n sample: (t: number) =\u003E from + (to - from) * e(t),\n }\n}\n\nexport function mapInterpolator(i: Interpolator, from: number, to: number) {\n return {\n end: to,\n sample: (t: number) =\u003E from + (to - from) * i.sample(t),\n }\n}\n\nexport type Interpolator = ReturnType\u003Ctypeof interpolator\u003E\n\nexport function sequence(a: Interpolator, b: Interpolator) {\n const sample = (t: number) =\u003E {\n if (t \u003C 0.5) {\n return a.sample(t * 2)\n } else {\n return b.sample((t - 0.5) * 2)\n }\n }\n\n return {\n sample,\n end: b.end,\n }\n}\n\nfunction sequenceN(...i: Interpolator[]) {\n const n = i.length\n\n const sample = (t: number) =\u003E {\n const slice = 1 \u002F n\n const index = t % slice\n \u002F\u002F cut t into n segments\n \u002F\u002F pick correct interpolator based on t\n }\n}\n\nexport type Animatable = number\n\nexport type ObservableSource = {\n changes: Subject\u003CAnimatable\u003E\n value: () =\u003E Animatable\n swap: (swapFn: (v: Animatable) =\u003E Animatable) =\u003E void\n set: (v: Animatable) =\u003E void\n}\n\nexport const useObservable = (v: Animatable): ObservableSource =\u003E {\n const val = React.useRef(v)\n\n const subject = new Subject\u003CAnimatable\u003E()\n\n return {\n changes: subject,\n value: () =\u003E val.current,\n swap: (swapFn: (v: Animatable) =\u003E Animatable) =\u003E {\n val.current = swapFn(val.current)\n subject.next(val.current)\n },\n set: (v: Animatable) =\u003E {\n val.current = v\n subject.next(v)\n },\n }\n}\n\nexport const useObservableState = (v: Animatable): ObservableSource =\u003E {\n const [val, setVal] = React.useState(v)\n\n const subject = React.useRef(new Subject\u003CAnimatable\u003E())\n\n return React.useMemo(\n () =\u003E ({\n changes: subject.current,\n value: () =\u003E val,\n swap: (swapFn: (v: Animatable) =\u003E Animatable) =\u003E {\n subject.current.next(swapFn(val))\n setVal(swapFn)\n },\n set: (v: Animatable) =\u003E {\n subject.current.next(v)\n setVal(v)\n },\n }),\n [val, setVal],\n )\n}\n\n\u002F\u002F frames$.subscribe(console.log)\n\nexport const useAnimation = (source: ObservableSource, interpolator: Interpolator, duration: number, sink: (v: Animatable) =\u003E void) =\u003E {\n const underlying = React.useRef(source.value())\n\n React.useEffect(() =\u003E {\n sink(underlying.current)\n\n const sub = source.changes\n .pipe(\n switchMap((v) =\u003E {\n const baseTime = Date.now()\n\n return concat(\n frames$.pipe(\n share(),\n map((dt) =\u003E (Date.now() - baseTime) \u002F duration),\n takeWhile((t) =\u003E t \u003C 1),\n ),\n of(1),\n ).pipe(map(mapInterpolator(interpolator, underlying.current, v).sample))\n }),\n )\n .subscribe((v) =\u003E {\n underlying.current = v\n sink(v)\n })\n\n return () =\u003E {\n sub.unsubscribe()\n }\n }, [duration, source, sink, interpolator])\n}\n","id":"mod_BS7X9ouMspinWAfXoC2MJf","is_binary":false,"title":"useAnimation.ts","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"HyMWHasSsZK","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"H17r6oBjbF"},{"code":"import { useEffect } from \"react\"\nimport { EMPTY, fromEvent, interval, merge } from \"rxjs\"\nimport { distinctUntilChanged, filter, groupBy, map, mergeAll, switchMap } from \"rxjs\u002Foperators\"\n\nexport const keydown$ = fromEvent\u003CKeyboardEvent\u003E(document, \"keydown\")\nexport const keyup$ = fromEvent\u003CKeyboardEvent\u003E(document, \"keyup\")\nexport const key$ = merge(keydown$, keyup$).pipe(\n groupBy((e: KeyboardEvent) =\u003E e.keyCode),\n map((group: any) =\u003E\n group.pipe(\n distinctUntilChanged(\n (a, b) =\u003E a === b,\n (e: KeyboardEvent) =\u003E e.type,\n ),\n ),\n ),\n mergeAll(),\n)\n\nfunction useKeyEvents(key: number, eventType: string, sink: () =\u003E void) {\n useEffect(() =\u003E {\n const s = key$\n .pipe(filter((ev) =\u003E (ev as KeyboardEvent).keyCode === key && (ev as KeyboardEvent).type === eventType && !(ev as KeyboardEvent).repeat))\n .subscribe(sink)\n return () =\u003E s.unsubscribe()\n }, [key, eventType, sink])\n}\n\nexport function useKeyDown(key: number, sink: () =\u003E void) {\n useKeyEvents(key, \"keydown\", sink)\n}\n\nexport function useKeyUp(key: number, sink: () =\u003E void) {\n useKeyEvents(key, \"keyup\", sink)\n}\n\nexport function useKeyHeld(key: number, intervalMs: number, sink: () =\u003E void) {\n useEffect(() =\u003E {\n const s = key$\n .pipe(\n filter((ev) =\u003E (ev as KeyboardEvent).keyCode === key),\n switchMap((ev) =\u003E {\n const kev = ev as KeyboardEvent\n if (kev.type === \"keydown\") {\n return interval(intervalMs)\n } else {\n return EMPTY\n }\n }),\n )\n .subscribe(sink)\n return () =\u003E s.unsubscribe()\n }, [key, intervalMs, sink])\n}\n","id":"mod_VE8nwDjjh6jRKwwSkGVYGK","is_binary":false,"title":"keyStream.ts","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"SyXbrpoHobK","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"By4HTjBi-Y"},{"code":"const KEYS = {\n \"0\": 48,\n \"1\": 49,\n \"2\": 50,\n \"3\": 51,\n \"4\": 52,\n \"5\": 53,\n \"6\": 54,\n \"7\": 55,\n \"8\": 56,\n \"9\": 57,\n backspace: 8,\n tab: 9,\n enter: 13,\n shift: 16,\n ctrl: 17,\n alt: 18,\n pause_break: 19,\n caps_lock: 20,\n escape: 27,\n page_up: 33,\n page_down: 34,\n end: 35,\n home: 36,\n left_arrow: 37,\n up_arrow: 38,\n right_arrow: 39,\n down_arrow: 40,\n insert: 45,\n delete: 46,\n a: 65,\n b: 66,\n c: 67,\n d: 68,\n e: 69,\n f: 70,\n g: 71,\n h: 72,\n i: 73,\n j: 74,\n k: 75,\n l: 76,\n m: 77,\n n: 78,\n o: 79,\n p: 80,\n q: 81,\n r: 82,\n s: 83,\n t: 84,\n u: 85,\n v: 86,\n w: 87,\n x: 88,\n y: 89,\n z: 90,\n left_window_key: 91,\n right_window_key: 92,\n select_key: 93,\n numpad_0: 96,\n numpad_1: 97,\n numpad_2: 98,\n numpad_3: 99,\n numpad_4: 100,\n numpad_5: 101,\n numpad_6: 102,\n numpad_7: 103,\n numpad_8: 104,\n numpad_9: 105,\n multiply: 106,\n add: 107,\n subtract: 109,\n decimal_point: 110,\n divide: 111,\n f1: 112,\n f2: 113,\n f3: 114,\n f4: 115,\n f5: 116,\n f6: 117,\n f7: 118,\n f8: 119,\n f9: 120,\n f10: 121,\n f11: 122,\n f12: 123,\n num_lock: 144,\n scroll_lock: 145,\n semi_colon: 186,\n equal_sign: 187,\n comma: 188,\n dash: 189,\n period: 190,\n forward_slash: 191,\n grave_accent: 192,\n open_bracket: 219,\n back_slash: 220,\n close_braket: 221,\n single_quote: 222,\n}\n\nexport default KEYS\n","id":"mod_6JnHeR9uLJMWNNkp7qkxie","is_binary":false,"title":"keys.ts","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"H1NWSToBsWY","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"By4HTjBi-Y"},{"code":"import React from \"react\"\nimport { fromEvent, identity, noop } from \"rxjs\"\nimport { map, pairwise, sampleTime, share, throttle } from \"rxjs\u002Foperators\"\n\nexport const mousemove$ = fromEvent\u003CMouseEvent\u003E(document, \"mousemove\").pipe(\n map((ev: MouseEvent) =\u003E [ev.clientX, ev.clientY]),\n share(),\n)\nexport const mousemovedelta$ = mousemove$.pipe(\n pairwise(),\n map(([[ax, ay], [bx, by]]) =\u003E [bx - ax, by - ay]),\n share(),\n)\nexport const mousemovenormalised$ = mousemove$.pipe(\n map(([x, y]) =\u003E [x \u002F document.documentElement.clientWidth, y \u002F document.documentElement.clientHeight]),\n share(),\n)\n\nexport function useMouseMove(sink: (p: number[]) =\u003E void, throttleMs?: number) {\n React.useEffect(() =\u003E {\n const s = mousemove$.pipe(throttleMs ? sampleTime(throttleMs || 0) : identity).subscribe(sink)\n\n return () =\u003E s.unsubscribe()\n }, [sink, throttleMs])\n}\n\nexport function useMouseMoveNormalised(sink: (p: number[]) =\u003E void, throttleMs?: number) {\n React.useEffect(() =\u003E {\n const s = mousemove$\n .pipe(\n throttleMs ? sampleTime(throttleMs || 0) : identity,\n map(([x, y]) =\u003E [x \u002F document.documentElement.clientWidth, y \u002F document.documentElement.clientHeight]),\n )\n .subscribe(sink)\n\n return () =\u003E s.unsubscribe()\n }, [sink, throttleMs])\n}\n\nexport function useMouseDelta(sink: (d: number[]) =\u003E void, throttleMs?: number) {\n React.useEffect(() =\u003E {\n const s = mousemovedelta$.pipe(throttleMs ? sampleTime(throttleMs || 0) : identity).subscribe(sink)\n\n return () =\u003E s.unsubscribe()\n }, [sink, throttleMs])\n}\n","id":"mod_DokLTKkz4Sw8aSGwVHtkev","is_binary":false,"title":"mouseStream.ts","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"SJSbBpjrjWF","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"By4HTjBi-Y"},{"code":"import * as React from \"react\"\nimport { useCallback, useEffect } from \"react\"\n\nexport const TICK = 16\ntype Mapping = {\n [action: string]: number[]\n}\ntype MappingT = {\n [keyCode: number]: string[]\n}\ntype InputCallbackMap\u003CM extends Mapping\u003E = {\n [Property in keyof Partial\u003CM\u003E]: {\n onPressed?: () =\u003E void\n onHeld?: () =\u003E void\n onReleased?: () =\u003E void\n }\n}\nfunction tryGet\u003CU, V extends { [k: string]: U }\u003E(data: V, key: keyof V, def: U) {\n if (data[key]) {\n return data[key]\n } else {\n ;(data as any)[key] = def\n return data[key]\n }\n}\nfunction transposeMapping(mapping: Mapping): MappingT {\n const t: any = {}\n for (let action in mapping) {\n const keyCodes = mapping[action]\n keyCodes.forEach((k) =\u003E tryGet(t, k, []).push(action))\n }\n return t\n}\ntype InputState = {\n pressed: boolean\n released: boolean\n held: boolean\n}\n\ntype InputMapKey\u003CT\u003E = keyof T\n\ntype InputMapState\u003CT\u003E = {\n [Property in InputMapKey\u003CT\u003E]: InputState\n}\n\nfunction createInputMap\u003CM extends Mapping\u003E(mapping: M) {\n const keys = Object.keys(mapping) as InputMapKey\u003CM\u003E[]\n const map = {} as InputMapState\u003CM\u003E\n\n for (const k of keys) {\n map[k] = { pressed: false, released: false, held: false }\n }\n\n return map as InputMapState\u003CM\u003E\n}\ntype InputAction\u003CM extends Mapping\u003E =\n | { type: \"pressed\"; action: keyof M }\n | { type: \"released\"; action: keyof M }\n | { type: \"released-timeout\"; action: keyof M }\n | { type: \"pressed-timeout\"; action: keyof M }\nfunction inputReducer\u003CM extends Mapping\u003E(state: InputMapState\u003CM\u003E, action: InputAction\u003CM\u003E) {\n switch (action.type) {\n case \"pressed\":\n return {\n ...state,\n [action.action]: { pressed: true, released: false, held: true },\n }\n case \"released\":\n return {\n ...state,\n [action.action]: { released: true, pressed: false, held: false },\n }\n case \"released-timeout\":\n return {\n ...state,\n [action.action]: { ...state[action.action], released: false },\n }\n case \"pressed-timeout\":\n return {\n ...state,\n [action.action]: { ...state[action.action], pressed: false },\n }\n }\n}\nexport function useInput\u003CM extends Mapping\u003E(mapping: M, callbacks: InputCallbackMap\u003CM\u003E = {} as InputCallbackMap\u003CM\u003E) {\n const mappingT = React.useMemo(() =\u003E transposeMapping(mapping), [mapping])\n const held = React.useRef({} as { [Property in InputMapKey\u003CM\u003E]: NodeJS.Timer })\n\n const [state, dispatch] = React.useReducer(\n inputReducer,\n React.useMemo(() =\u003E createInputMap(mapping), [mapping]),\n )\n\n const onKeyDown = useCallback(\n (ev: KeyboardEvent) =\u003E {\n if (mappingT[ev.keyCode] && !ev.repeat) {\n mappingT[ev.keyCode].forEach((a: InputMapKey\u003CM\u003E) =\u003E {\n dispatch({ type: \"pressed\", action: a as any })\n setTimeout(() =\u003E {\n dispatch({ type: \"pressed-timeout\", action: a as any })\n }, TICK)\n\n const onPressed = callbacks[a]?.onPressed\n if (onPressed) {\n onPressed()\n }\n\n if (held.current[a]) {\n clearInterval(held.current[a])\n }\n held.current[a] = setInterval(() =\u003E {\n const onHeld = callbacks[a]?.onHeld\n if (onHeld) {\n onHeld()\n }\n }, TICK)\n })\n }\n },\n [dispatch, mappingT, callbacks],\n )\n const onKeyUp = useCallback(\n (ev: KeyboardEvent) =\u003E {\n if (mappingT[ev.keyCode] && !ev.repeat) {\n mappingT[ev.keyCode].forEach((a) =\u003E {\n dispatch({ type: \"released\", action: a })\n setTimeout(() =\u003E {\n dispatch({ type: \"released-timeout\", action: a })\n }, TICK)\n if (held.current[a]) {\n clearInterval(held.current[a])\n }\n\n const onReleased = callbacks[a]?.onReleased\n if (onReleased) {\n onReleased()\n }\n })\n }\n },\n [dispatch, mappingT, callbacks],\n )\n\n useEffect(() =\u003E {\n document.addEventListener(\"keydown\", onKeyDown)\n document.addEventListener(\"keyup\", onKeyUp)\n\n return () =\u003E {\n document.removeEventListener(\"keydown\", onKeyDown)\n document.removeEventListener(\"keyup\", onKeyUp)\n }\n }, [onKeyDown, onKeyUp])\n\n return state\n}\n","id":"mod_RjoaL4X6iqT5TrWqCErc48","is_binary":false,"title":"useInput.ts","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:09","upload_id":null,"shortid":"ByI-BpsSiWY","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":"By4HTjBi-Y"},{"code":"{\n \"name\": \"use-animation-demo\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"keywords\": [],\n \"main\": \"src\u002Findex.js\",\n \"dependencies\": {\n \"@react-three\u002Fdrei\": \"4.1.8\",\n \"@react-three\u002Ffiber\": \"6.0.14\",\n \"@types\u002Frx\": \"4.1.2\",\n \"framer-motion\": \"^4.1.17\",\n \"react\": \"17.0.2\",\n \"react-dom\": \"17.0.2\",\n \"react-scripts\": \"4.0.3\",\n \"rxjs\": \"6.6.7\",\n \"simplex-noise\": \"2.4.0\",\n \"three\": \"0.127.0\"\n },\n \"devDependencies\": {\n \"@types\u002Freact\": \"^17.0.19\",\n \"@types\u002Fthree\": \"^0.131.0\",\n \"typescript\": \"^4.3.5\"\n },\n \"scripts\": {\n \"start\": \"react-scripts start\",\n \"build\": \"react-scripts build\",\n \"test\": \"react-scripts test --env=jsdom\",\n \"eject\": \"react-scripts eject\"\n },\n \"browserslist\": [\n \"\u003E0.2%\",\n \"not dead\",\n \"not ie \u003C= 11\",\n \"not op_mini all\"\n ]\n}","id":"mod_5KbX1ch8RAWAamPet1SB6h","is_binary":false,"title":"package.json","sha":null,"inserted_at":"2021-08-31T06:27:09","updated_at":"2021-08-31T06:27:43","upload_id":null,"shortid":"rkiHpsBo-t","source_id":"src_HKQgZXwK4PYVWN8x1mWwMr","directory_shortid":null}],"owned":false,"custom_template":null,"privacy":0};