├── .eslintignore ├── .eslintrc.json ├── .gitignore ├── .prettierrc.json ├── README.md ├── World ├── systems │ ├── composer.ts │ ├── index.ts │ ├── pass │ │ ├── chess.ts │ │ ├── dotsGrid.ts │ │ ├── dotsLanding.ts │ │ ├── gradient.ts │ │ ├── grain.ts │ │ ├── helpers.ts │ │ ├── index.ts │ │ ├── liquid.ts │ │ ├── liquidLarge.ts │ │ ├── masking.ts │ │ ├── silk.ts │ │ ├── stepped.ts │ │ ├── steppedWobble.ts │ │ ├── verner.ts │ │ └── zebra.ts │ └── renderer.ts └── things │ ├── camera.ts │ ├── index.ts │ └── scene.ts ├── app.vue ├── assets ├── css │ └── reset.css └── scss │ ├── global.scss │ └── variables.scss ├── components ├── AppHeader.vue ├── Footer.vue ├── ToolLayout.vue ├── glass │ ├── GlassControls.vue │ ├── GlassCss.vue │ ├── GlassImage.vue │ ├── GlassImageSegment.vue │ └── GlassWrapper.vue ├── landing │ ├── Hero.vue │ ├── Tool.vue │ └── Tools.vue ├── poster │ ├── AppContainer.vue │ ├── controls │ │ ├── ColorPicker.vue │ │ ├── ColorsPicker.vue │ │ ├── Controls.vue │ │ └── ExportOptions.vue │ └── scene │ │ ├── Scene.vue │ │ ├── SceneText.vue │ │ └── text │ │ └── TextTitle.vue └── progressive-blur │ ├── BlurredImage.vue │ ├── CSSDisplay.vue │ ├── Controls.vue │ ├── MainWrapper.vue │ ├── ProgressiveBlur.vue │ └── ProgressiveBlur2.vue ├── constants └── index.ts ├── helpers ├── fonts.ts ├── glass │ ├── images.ts │ └── index.ts └── progressive-blur │ ├── images.ts │ └── index.ts ├── layouts ├── default.vue └── tool.vue ├── nuxt.config.ts ├── package.json ├── pages ├── about.vue ├── glass │ └── index.vue ├── index.vue ├── poster │ ├── download.vue │ └── index.vue └── progressive-blur │ └── index.vue ├── plugins ├── useEmitter.ts └── useRandom.ts ├── public ├── android-chrome-192x192.png ├── android-chrome-512x512.png ├── apple-touch-icon.png ├── backdrop.png ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon.ico └── star.png ├── store └── scene.ts ├── tsconfig.json └── yarn.lock /.eslintignore: -------------------------------------------------------------------------------- 1 | .nuxt 2 | dist 3 | .output 4 | .vercel -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true 5 | }, 6 | "extends": [ 7 | "plugin:vue/vue3-recommended" 8 | ], 9 | "parser": "vue-eslint-parser", 10 | "parserOptions": { 11 | "ecmaVersion": "latest", 12 | "parser": "@typescript-eslint/parser" 13 | }, 14 | "plugins": [], 15 | "rules": { 16 | "vue/multi-word-component-names": 0 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Node template 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | # Bower dependency directory (https://bower.io/) 29 | bower_components 30 | 31 | # node-waf configuration 32 | .lock-wscript 33 | 34 | # Compiled binary addons (https://nodejs.org/api/addons.html) 35 | build/Release 36 | 37 | # Dependency directories 38 | node_modules/ 39 | jspm_packages/ 40 | 41 | # TypeScript v1 declaration files 42 | typings/ 43 | 44 | # Optional npm cache directory 45 | .npm 46 | 47 | # Optional eslint cache 48 | .eslintcache 49 | 50 | # Optional REPL history 51 | .node_repl_history 52 | 53 | # Output of 'npm pack' 54 | *.tgz 55 | 56 | # Yarn Integrity file 57 | .yarn-integrity 58 | 59 | # dotenv environment variables file 60 | .env 61 | .env.build 62 | 63 | # parcel-bundler cache (https://parceljs.org/) 64 | .cache 65 | 66 | # next.js build output 67 | .next 68 | 69 | # nuxt.js build output 70 | .nuxt 71 | .output 72 | 73 | # Nuxt generate 74 | dist 75 | 76 | # vuepress build output 77 | .vuepress/dist 78 | 79 | # Serverless directories 80 | .serverless 81 | 82 | # IDE / Editor 83 | .idea 84 | 85 | # Service worker 86 | sw.* 87 | 88 | # macOS 89 | .DS_Store 90 | 91 | # Vim swap files 92 | *.swp 93 | 94 | # Vercel 95 | .vercel 96 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nuxt Example 2 | 3 | Deploy your [Nuxt](https://nuxt.com) project to Vercel with zero configuration. 4 | 5 | [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/vercel/vercel/tree/main/examples/nuxtjs&template=nuxtjs) 6 | 7 | _Live Example: https://nuxtjs-template.vercel.app_ 8 | 9 | Look at the [Nuxt 3 documentation](https://v3.nuxtjs.org) to learn more. 10 | 11 | ## Setup 12 | 13 | Make sure to install the dependencies: 14 | 15 | ```bash 16 | # yarn 17 | yarn 18 | 19 | # npm 20 | npm install 21 | 22 | # pnpm 23 | pnpm install --shamefully-hoist 24 | ``` 25 | 26 | ## Development Server 27 | 28 | Start the development server on http://localhost:3000 29 | 30 | ```bash 31 | npm run dev 32 | ``` 33 | 34 | ## Production 35 | 36 | Build the application for production: 37 | 38 | ```bash 39 | npm run build 40 | ``` 41 | 42 | Locally preview production build: 43 | 44 | ```bash 45 | npm run preview 46 | ``` 47 | 48 | Checkout the [deployment documentation](https://nuxt.com/docs/getting-started/deployment#presets) for more information. 49 | -------------------------------------------------------------------------------- /World/systems/composer.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Camera, 3 | LinearFilter, 4 | RGBAFormat, 5 | Scene, 6 | WebGLRenderer, 7 | WebGLRenderTarget, 8 | } from "three"; 9 | import { HEIGHT, WIDTH } from "~/constants"; 10 | import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer.js"; 11 | import { 12 | createExportRenderer, 13 | createLandingRenderer, 14 | createRenderer, 15 | } from "~/World/systems/renderer"; 16 | import { getPasses } from "~/World/systems/pass"; 17 | import { Pass } from "three/examples/jsm/postprocessing/Pass"; 18 | 19 | function createBaseComposer(renderer: WebGLRenderer, passes: Pass[]) { 20 | const parameters = { 21 | minFilter: LinearFilter, 22 | magFilter: LinearFilter, 23 | format: RGBAFormat, 24 | stencilBuffer: false, 25 | }; 26 | const renderTarget = new WebGLRenderTarget(WIDTH, HEIGHT, parameters); 27 | const composer = new EffectComposer(renderer, renderTarget); 28 | composer.setSize(WIDTH, HEIGHT); 29 | passes.forEach((pass) => composer.addPass(pass)); 30 | return composer; 31 | } 32 | 33 | function createComposer(camera: Camera, passes: Pass[] = getPasses(camera)) { 34 | return createBaseComposer(createRenderer(), passes); 35 | } 36 | 37 | function createExportComposer( 38 | camera: Camera, 39 | passes: Pass[] = getPasses(camera), 40 | ) { 41 | return createBaseComposer(createExportRenderer(), passes); 42 | } 43 | 44 | export { createComposer, createExportComposer }; 45 | -------------------------------------------------------------------------------- /World/systems/index.ts: -------------------------------------------------------------------------------- 1 | export { createRenderer } from "./renderer"; 2 | -------------------------------------------------------------------------------- /World/systems/pass/chess.ts: -------------------------------------------------------------------------------- 1 | import { ShaderPass } from "three/examples/jsm/postprocessing/ShaderPass.js"; 2 | import { Camera } from "three"; 3 | import { baseShaderUniforms, baseUniforms } from "~/World/systems/pass/helpers"; 4 | 5 | const vertexShader = ` 6 | varying vec2 vUv; 7 | void main() { 8 | vUv = uv; 9 | gl_Position = projectionMatrix 10 | * modelViewMatrix 11 | * vec4( position, 1.0 ); 12 | } 13 | `; 14 | 15 | const fragmentShader = ` 16 | #define PI 3.14159265358979323846 17 | uniform float offset; 18 | varying vec2 vUv; 19 | ${baseShaderUniforms} 20 | 21 | // Function to generate a simple procedural noise 22 | float simpleNoise(vec2 st) 23 | { 24 | return fract(sin(dot(st, vec2(12.9898, 78.233))) * 43758.5453); 25 | } 26 | 27 | void main() 28 | { 29 | ivec2 cell = ivec2(floor(vUv * 8.0)); 30 | bool isBlack = (cell.x + cell.y) % 2 == 1; 31 | float noise = simpleNoise(vUv * position.x * 00.1); 32 | vec2 perturb = vec2(noise * 0.1 - 0.05, noise * position.y * 00.1 - 0.05); 33 | vec2 distortedTexCoord = vUv + perturb; 34 | 35 | ivec2 distortedCell = ivec2(floor(distortedTexCoord * 8.0)); 36 | bool isDistortedBlack = (distortedCell.x + distortedCell.y) % 2 == 1; 37 | vec3 color = isDistortedBlack ? colors.background.rgb : colors.color.rgb; 38 | 39 | gl_FragColor = vec4(color, 1.0); 40 | } 41 | 42 | `; 43 | 44 | function createChessPass(camera: Camera) { 45 | const { $random } = useNuxtApp(); 46 | const effect = { 47 | uniforms: { 48 | ...baseUniforms(), 49 | offset: { value: 10 + $random.$getRandom() * 50 }, 50 | }, 51 | vertexShader: vertexShader, 52 | fragmentShader: fragmentShader, 53 | name: "ChessPass", 54 | }; 55 | const pass = new ShaderPass(effect); 56 | pass.renderToScreen = true; 57 | return pass; 58 | } 59 | 60 | export { createChessPass }; 61 | -------------------------------------------------------------------------------- /World/systems/pass/dotsGrid.ts: -------------------------------------------------------------------------------- 1 | import { ShaderPass } from "three/examples/jsm/postprocessing/ShaderPass.js"; 2 | import { Camera } from "three"; 3 | import { baseShaderUniforms, baseUniforms } from "~/World/systems/pass/helpers"; 4 | 5 | const vertexShader = ` 6 | varying vec2 vUv; 7 | void main() { 8 | vUv = uv; 9 | gl_Position = projectionMatrix 10 | * modelViewMatrix 11 | * vec4( position, 1.0 ); 12 | } 13 | `; 14 | 15 | const fragmentShader = ` 16 | #define PI 3.14159265358979323846 17 | uniform float offset; 18 | varying vec2 vUv; 19 | ${baseShaderUniforms} 20 | 21 | float random (in vec2 _st) { 22 | return fract(sin(dot(_st.xy, 23 | vec2(12.9898,78.233)))* 24 | 43758.5453123); 25 | } 26 | 27 | vec2 pattern(in vec2 _st, in float _index){ 28 | _index = fract(((_index-sin(position.x+2.))*sin(position.y-1.))); 29 | if (_index > 0.804) { 30 | _st = vec2(1.560-_st.x, 1.536-_st.y); 31 | } 32 | else { 33 | _st = vec2(2.400-_st.x, 1.976-_st.y); 34 | } 35 | return _st; 36 | } 37 | 38 | void main() { 39 | vec2 st = vUv; 40 | float aspect = width / height; 41 | st.x *= aspect; 42 | st *= offset; 43 | vec2 ipos = floor(st); 44 | vec2 fpos = fract(st); 45 | vec2 tile = pattern(fpos, random( ipos )); 46 | float color = 0.0; 47 | color = step(length(tile-vec2(1.,1.)),0.4); 48 | vec3 mixed = mix(colors.color.rgb, colors.background.rgb, clamp(1. - color, 0.0, 1.0)); 49 | gl_FragColor = vec4(mixed, 1.); 50 | } 51 | `; 52 | 53 | function createDotsPass(camera: Camera) { 54 | const { $random } = useNuxtApp(); 55 | const effect = { 56 | uniforms: { 57 | ...baseUniforms(), 58 | offset: { value: 10 + $random.$getRandom() * 50 }, 59 | }, 60 | vertexShader: vertexShader, 61 | fragmentShader: fragmentShader, 62 | name: "DotsPass", 63 | }; 64 | const pass = new ShaderPass(effect); 65 | pass.renderToScreen = true; 66 | return pass; 67 | } 68 | 69 | export { createDotsPass }; 70 | -------------------------------------------------------------------------------- /World/systems/pass/dotsLanding.ts: -------------------------------------------------------------------------------- 1 | import { ShaderPass } from "three/examples/jsm/postprocessing/ShaderPass.js"; 2 | import { useSceneStore } from "~/store/scene"; 3 | 4 | const vertexShader = ` 5 | varying vec2 vUv; 6 | void main() { 7 | vUv = uv; 8 | gl_Position = projectionMatrix 9 | * modelViewMatrix 10 | * vec4( position, 1.0 ); 11 | } 12 | `; 13 | 14 | const fragmentShader = ` 15 | vec3 white = vec3(1.0, 1.0, 1.0); 16 | vec3 black = vec3(0.0, 0.0, 0.0); 17 | float pixelWidth = 0.368; 18 | float thickness = 0.980; 19 | vec2 st; 20 | uniform float offset; 21 | 22 | float random(in vec2 st) { 23 | return fract(sin(dot(st.xy, 24 | vec2(12.9898,78.233)))* 25 | 43758.5453123); 26 | } 27 | 28 | vec3 drawCircle(vec2 center, float radius, vec3 color) { 29 | float r = sqrt(pow(st.xy.xy.x - center.x, 2.) + pow(st.xy.xy.y - center.y, 2.)); 30 | float delta = r - radius; 31 | vec3 inside = color; 32 | float blend = smoothstep(0., pixelWidth, abs(delta) - thickness); 33 | return mix(white, inside, blend); 34 | } 35 | 36 | vec2 pattern(in vec2 _st, in float _index){ 37 | _index = fract(((_index-1.660)*(10. / 10.))); 38 | if (_index > 0.860) { 39 | _st = vec2(0.5-_st.x, 0.5-_st.y); 40 | } 41 | else { 42 | _st = vec2(1.216-_st.x, 1.680-_st.y); 43 | } 44 | return _st; 45 | } 46 | 47 | void main() { 48 | vec2 st = gl_FragCoord.xy; 49 | st *= offset; 50 | vec2 ipos = floor(st); 51 | vec2 fpos = fract(st); 52 | vec2 tile = pattern(fpos, random( ipos )); 53 | vec3 color = drawCircle(tile, 1.512, vec3(random(ipos), cos(ipos.y), sin(ipos.x*random( ipos )))); 54 | gl_FragColor = vec4(color, 1.0); 55 | } 56 | `; 57 | 58 | function createLandingPass() { 59 | const { $random } = useNuxtApp(); 60 | const effect = { 61 | uniforms: { 62 | offset: { value: $random.$getRandom() / 20 }, 63 | }, 64 | vertexShader: vertexShader, 65 | fragmentShader: fragmentShader, 66 | }; 67 | const pass = new ShaderPass(effect); 68 | pass.renderToScreen = true; 69 | return pass; 70 | } 71 | 72 | export { createLandingPass }; 73 | -------------------------------------------------------------------------------- /World/systems/pass/gradient.ts: -------------------------------------------------------------------------------- 1 | import { Camera } from "three"; 2 | import { ShaderPass } from "three/examples/jsm/postprocessing/ShaderPass"; 3 | import { HEIGHT, WIDTH } from "~/constants"; 4 | import { 5 | baseShaderUniforms, 6 | baseUniforms, 7 | translateColorspace, 8 | } from "~/World/systems/pass/helpers"; 9 | 10 | const vertexShader = ` 11 | varying vec2 vUv; 12 | void main() { 13 | vUv = uv; 14 | gl_Position = projectionMatrix 15 | * modelViewMatrix 16 | * vec4( position, 1.0 ); 17 | } 18 | `; 19 | 20 | const fragmentShader = ` 21 | uniform float offset; 22 | varying vec2 vUv; 23 | ${baseShaderUniforms} 24 | ${translateColorspace} 25 | void main() { 26 | vec2 center = vec2(0.5, 0.5 - (borders.top / (borders.bottom * 100.))); 27 | float aspect = width / height; 28 | vec2 uv = vUv; 29 | uv.x *= aspect; 30 | center.x *= aspect; 31 | vec4 gradientColor = vec4(0.5 - distance(center, uv), 0.0 + 0.5 * sin(position.x), 1.0 + 0.5 * cos(position.y), 1.0); 32 | vec3 hsv = rgb2hsv(gradientColor.rgb); 33 | hsv.x *= offset; 34 | gl_FragColor = vec4(hsv2rgb(hsv), 1.0); 35 | } 36 | `; 37 | 38 | function createGradientPass(camera: Camera) { 39 | const { $random } = useNuxtApp(); 40 | const effect = { 41 | uniforms: { 42 | ...baseUniforms(), 43 | offset: { value: $random.$getRandom() * 10 }, 44 | }, 45 | vertexShader: vertexShader, 46 | fragmentShader: fragmentShader, 47 | name: "GradientPass", 48 | }; 49 | const pass = new ShaderPass(effect); 50 | pass.renderToScreen = true; 51 | return pass; 52 | } 53 | 54 | export { createGradientPass }; 55 | -------------------------------------------------------------------------------- /World/systems/pass/grain.ts: -------------------------------------------------------------------------------- 1 | import { ShaderPass } from "three/examples/jsm/postprocessing/ShaderPass.js"; 2 | 3 | const vertexShader = ` 4 | varying vec2 vUv; 5 | void main() { 6 | vUv = uv; 7 | gl_Position = projectionMatrix 8 | * modelViewMatrix 9 | * vec4( position, 1.0 ); 10 | } 11 | `; 12 | 13 | const fragmentShader = ` 14 | uniform float amount; 15 | uniform sampler2D tDiffuse; 16 | varying vec2 vUv; 17 | 18 | float random( vec2 p ) 19 | { 20 | vec2 K1 = vec2( 21 | 23.14069263277926, // e^pi (Gelfond's constant) 22 | 2.665144142690225 // 2^sqrt(2) (Gelfond–Schneider constant) 23 | ); 24 | return fract( cos( dot(p,K1) ) * 12345.6789 ); 25 | } 26 | void main() { 27 | vec4 color = texture2D( tDiffuse, vUv ); 28 | vec2 uvRandom = vUv; 29 | uvRandom.y *= random(vec2(uvRandom.y,amount)); 30 | color.rgb += random(uvRandom)*0.15; 31 | gl_FragColor = vec4( color ); 32 | } 33 | `; 34 | 35 | function createGrainPass() { 36 | const { $random } = useNuxtApp(); 37 | const counter = $random.$getRandom(); 38 | const grainEffect = { 39 | uniforms: { 40 | tDiffuse: { value: null }, 41 | amount: { value: counter }, 42 | }, 43 | vertexShader: vertexShader, 44 | fragmentShader: fragmentShader, 45 | }; 46 | const grainPass = new ShaderPass(grainEffect); 47 | grainPass.renderToScreen = true; 48 | return grainPass; 49 | } 50 | 51 | export { createGrainPass }; 52 | -------------------------------------------------------------------------------- /World/systems/pass/helpers.ts: -------------------------------------------------------------------------------- 1 | import { Vector4 } from "three"; 2 | import { useSceneStore } from "~/store/scene"; 3 | import { HEIGHT, WIDTH } from "~/constants"; 4 | 5 | export const translateColorspace = ` 6 | vec3 rgb2hsv(vec3 c) { 7 | vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); 8 | vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); 9 | vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); 10 | 11 | float d = q.x - min(q.w, q.y); 12 | float e = 1.0e-10; 13 | return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); 14 | } 15 | 16 | vec3 hsv2rgb(vec3 c) { 17 | vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); 18 | vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); 19 | return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); 20 | }`; 21 | 22 | export const baseUniforms = () => { 23 | const sceneStore = useSceneStore(); 24 | const showBorders = 25 | sceneStore.scene(sceneStore.activeScene!).showBorders ?? true; 26 | return { 27 | colors: { 28 | value: { 29 | background: 30 | sceneStore.scene(sceneStore.activeScene!).background ?? 31 | new Vector4(1, 1, 1, 1), 32 | color: 33 | sceneStore.scene(sceneStore.activeScene!).color ?? 34 | new Vector4(0, 0, 0, 1), 35 | }, 36 | }, 37 | borders: { 38 | value: { 39 | top: 228 / HEIGHT, 40 | right: 32 / WIDTH, 41 | bottom: 228 / HEIGHT, 42 | left: 32 / WIDTH, 43 | show: showBorders ?? true, 44 | }, 45 | }, 46 | position: { 47 | x: 0, 48 | y: 0, 49 | }, 50 | width: { value: WIDTH }, 51 | height: { value: HEIGHT }, 52 | }; 53 | }; 54 | 55 | export const baseShaderUniforms = ` 56 | struct ColorData { 57 | vec4 background; 58 | vec4 color; 59 | }; 60 | uniform ColorData colors; 61 | 62 | struct BorderData { 63 | float top; 64 | float right; 65 | float bottom; 66 | float left; 67 | bool show; 68 | }; 69 | uniform BorderData borders; 70 | 71 | struct PositionData { 72 | float x; 73 | float y; 74 | }; 75 | uniform PositionData position; 76 | 77 | uniform float width; 78 | uniform float height; 79 | `; 80 | -------------------------------------------------------------------------------- /World/systems/pass/index.ts: -------------------------------------------------------------------------------- 1 | import { createLiquidPass } from "~/World/systems/pass/liquid"; 2 | import { createMaskingPass } from "~/World/systems/pass/masking"; 3 | import { createGrainPass } from "~/World/systems/pass/grain"; 4 | import { Camera, Scene } from "three"; 5 | import { createZebraPass } from "~/World/systems/pass/zebra"; 6 | import { createLiquidLargePass } from "~/World/systems/pass/liquidLarge"; 7 | import { createGradientPass } from "~/World/systems/pass/gradient"; 8 | import { createSteppedPass } from "~/World/systems/pass/stepped"; 9 | import { createSilkPass } from "~/World/systems/pass/silk"; 10 | import { createDotsPass } from "~/World/systems/pass/dotsGrid"; 11 | import { createChessPass } from "~/World/systems/pass/chess"; 12 | import { createVernerPass } from "~/World/systems/pass/verner"; 13 | import { createSteppedWobblePass } from "~/World/systems/pass/steppedWobble"; 14 | 15 | const basePasses = (camera: Camera) => { 16 | return [ 17 | createLiquidPass(camera), 18 | createZebraPass(camera), 19 | createLiquidLargePass(camera), 20 | createGradientPass(camera), 21 | createSteppedPass(camera), 22 | createSteppedWobblePass(camera), 23 | createSilkPass(camera), 24 | createDotsPass(camera), 25 | createChessPass(camera), 26 | createVernerPass(camera), 27 | ]; 28 | }; 29 | 30 | export const getPasses = (camera: Camera) => { 31 | const { $random } = useNuxtApp(); 32 | const base = basePasses(camera); 33 | return [base[Math.floor($random.$getRandom() * base.length)]]; 34 | }; 35 | -------------------------------------------------------------------------------- /World/systems/pass/liquid.ts: -------------------------------------------------------------------------------- 1 | import { ShaderPass } from "three/examples/jsm/postprocessing/ShaderPass.js"; 2 | import { Camera } from "three"; 3 | import { baseShaderUniforms, baseUniforms } from "~/World/systems/pass/helpers"; 4 | 5 | const vertexShader = ` 6 | varying vec2 vUv; 7 | void main() { 8 | vUv = uv; 9 | gl_Position = projectionMatrix 10 | * modelViewMatrix 11 | * vec4( position, 1.0 ); 12 | } 13 | `; 14 | 15 | const fragmentShader = ` 16 | uniform float offset; 17 | varying vec2 vUv; 18 | ${baseShaderUniforms} 19 | 20 | mat2 rot(float deg) 21 | { 22 | return mat2(cos(deg),-sin(deg), 23 | sin(deg), cos(deg)); 24 | 25 | } 26 | float random( vec2 p ) 27 | { 28 | vec2 K1 = vec2( 29 | 23.14069263277926, 30 | 2.665144142690225 31 | ); 32 | return fract( cos( dot(p,K1) ) * 12345.6789 ); 33 | } 34 | 35 | void main() { 36 | vec2 uv = vUv; 37 | float t = position.x + position.y + offset; 38 | 39 | uv-=.5 * offset; 40 | uv*=5. * offset; 41 | 42 | uv*=rot(uv.y/5.-t*.15); 43 | uv-=sin(sqrt(uv.x*uv.x+uv.y*uv.y)-t*2.)*3.; 44 | uv.y+=sin(uv.x-t)*1.2; 45 | uv-=sin(sqrt(uv.x*uv.x+uv.y*uv.y)+t)*.6; 46 | uv.x+=sin(uv.y*1.4+t)*.6; 47 | 48 | uv*=rot(uv.x/5.-t*.8); 49 | uv.x/=length(.75*uv); 50 | uv.y/=length(.75*uv); 51 | 52 | gl_FragColor = vec4( vec3(cos(uv.x+uv.y-t*.7), cos(uv.x+uv.y-t*.6), cos(uv.x+uv.y-t*.8)), 1. ); 53 | } 54 | `; 55 | 56 | function createLiquidPass(camera: Camera) { 57 | const { $random } = useNuxtApp(); 58 | const liquidEffect = { 59 | uniforms: { 60 | ...baseUniforms(), 61 | offset: { value: $random.$getRandom() * 10 }, 62 | }, 63 | vertexShader: vertexShader, 64 | fragmentShader: fragmentShader, 65 | name: "LiquidPass", 66 | }; 67 | const liquidPass = new ShaderPass(liquidEffect); 68 | liquidPass.renderToScreen = true; 69 | return liquidPass; 70 | } 71 | 72 | export { createLiquidPass }; 73 | -------------------------------------------------------------------------------- /World/systems/pass/liquidLarge.ts: -------------------------------------------------------------------------------- 1 | import { ShaderPass } from "three/examples/jsm/postprocessing/ShaderPass.js"; 2 | import { Camera } from "three"; 3 | import { baseShaderUniforms, baseUniforms } from "~/World/systems/pass/helpers"; 4 | 5 | const vertexShader = ` 6 | varying vec2 vUv; 7 | void main() { 8 | vUv = uv; 9 | gl_Position = projectionMatrix 10 | * modelViewMatrix 11 | * vec4( position, 1.0 ); 12 | } 13 | `; 14 | 15 | const fragmentShader = ` 16 | uniform float offset; 17 | varying vec2 vUv; 18 | ${baseShaderUniforms} 19 | 20 | mat2 rot(float deg) 21 | { 22 | return mat2(cos(deg),-sin(deg), 23 | sin(deg), cos(deg)); 24 | 25 | } 26 | float random( vec2 p ) 27 | { 28 | vec2 K1 = vec2( 29 | 23.14069263277926, 30 | 2.665144142690225 31 | ); 32 | return fract( cos( dot(p,K1) ) * 12345.6789 ); 33 | } 34 | 35 | void main() { 36 | float t = position.x + position.y + offset; 37 | vec2 uv = vUv; 38 | 39 | uv-=position.x * 0.1; 40 | uv*=position.y * 0.5; 41 | 42 | uv*=rot(uv.y/5.-t*.15); 43 | uv-=sin(sqrt(uv.x*uv.x+uv.y*uv.y)-t*2.)*3.; 44 | uv.y+=sin(uv.x-t)*1.2; 45 | uv-=sin(sqrt(uv.x*uv.x+uv.y*uv.y)+t)*.6; 46 | uv.x+=sin(uv.y*1.4+t)*.6; 47 | 48 | uv*=rot(uv.x/5.-t*.8); 49 | uv.x/=length(.75*uv); 50 | uv.y/=length(.75*uv); 51 | 52 | float value = cos(uv.x+uv.y-t*.6); 53 | gl_FragColor = vec4( vec3(cos(uv.x+uv.y-t*.7),cos(uv.x+uv.y-t*.6),cos(uv.x+uv.y-t*.8)), 1. ); 54 | } 55 | `; 56 | 57 | function createLiquidLargePass(camera: Camera) { 58 | const { $random } = useNuxtApp(); 59 | const liquidEffect = { 60 | uniforms: { 61 | ...baseUniforms(), 62 | offset: { value: $random.$getRandom() * 10 }, 63 | }, 64 | vertexShader: vertexShader, 65 | fragmentShader: fragmentShader, 66 | name: "liquidLargePass", 67 | }; 68 | const liquidPass = new ShaderPass(liquidEffect); 69 | liquidPass.renderToScreen = true; 70 | return liquidPass; 71 | } 72 | 73 | export { createLiquidLargePass }; 74 | -------------------------------------------------------------------------------- /World/systems/pass/masking.ts: -------------------------------------------------------------------------------- 1 | import { ShaderPass } from "three/examples/jsm/postprocessing/ShaderPass.js"; 2 | import { baseShaderUniforms, baseUniforms } from "~/World/systems/pass/helpers"; 3 | 4 | const vertexShader = ` 5 | varying vec2 vUv; 6 | void main() { 7 | vUv = uv; 8 | gl_Position = projectionMatrix 9 | * modelViewMatrix 10 | * vec4( position, 1.0 ); 11 | } 12 | `; 13 | 14 | const fragmentShader = ` 15 | uniform sampler2D tDiffuse; 16 | varying vec2 vUv; 17 | ${baseShaderUniforms} 18 | 19 | void main() { 20 | vec4 color = texture2D( tDiffuse, vUv ); 21 | vec2 uv = vUv; 22 | vec2 bl = step(vec2(borders.left, borders.bottom),uv); 23 | float padding = bl.x * bl.y; 24 | vec2 tr = step(vec2(borders.right, borders.top),1.0-uv); 25 | padding *= tr.x * tr.y; 26 | if (borders.show && step( 0.5, 1. - padding ) > 0.) { 27 | color *= colors.background; 28 | } 29 | gl_FragColor = color; 30 | } 31 | `; 32 | 33 | function createMaskingPass() { 34 | const maskingEffect = { 35 | uniforms: { 36 | ...baseUniforms(), 37 | tDiffuse: { value: null }, 38 | }, 39 | vertexShader: vertexShader, 40 | fragmentShader: fragmentShader, 41 | name: "MaskingPass", 42 | }; 43 | const maskingPass = new ShaderPass(maskingEffect); 44 | maskingPass.renderToScreen = true; 45 | return maskingPass; 46 | } 47 | 48 | export { createMaskingPass }; 49 | -------------------------------------------------------------------------------- /World/systems/pass/silk.ts: -------------------------------------------------------------------------------- 1 | import { Camera } from "three"; 2 | import { ShaderPass } from "three/examples/jsm/postprocessing/ShaderPass"; 3 | import { 4 | baseShaderUniforms, 5 | baseUniforms, 6 | translateColorspace, 7 | } from "~/World/systems/pass/helpers"; 8 | 9 | const vertexShader = ` 10 | varying vec2 vUv; 11 | void main() { 12 | vUv = uv; 13 | gl_Position = projectionMatrix 14 | * modelViewMatrix 15 | * vec4( position, 1.0 ); 16 | } 17 | `; 18 | 19 | const fragmentShader = ` 20 | uniform float offset; 21 | varying vec2 vUv; 22 | ${translateColorspace} 23 | ${baseShaderUniforms} 24 | #define rot(x) mat2(cos(x), -sin(x), sin(x), cos(x)) 25 | 26 | float heightS(vec2 p){ 27 | return sin(p.x)+sin(p.x+p.y)+cos(p.y)/1.5+sin(offset+p.x)+5.; 28 | } 29 | 30 | float map(vec3 p){ 31 | return p.y-heightS(p.xz); 32 | } 33 | void main(){ 34 | vec2 uv = vUv; 35 | vec3 ray = normalize(vec3(uv,1.)); 36 | ray.yz *= rot((sin(position.y)/3.+1.5)); 37 | ray.xz *= rot((sin(position.x)/2.+1.)/5.); 38 | 39 | float t = 0.; 40 | for(int i = 0; i < 29 ; ++i) 41 | t += map(vec3(position.x,0.,position.x/2.)+ray*t)*.5; 42 | 43 | float fog = 1./(1.+t*t*.005); 44 | vec3 fc = vec3(fog*fog, fog/2., fog); 45 | vec3 hsl = rgb2hsv(fc); 46 | hsl.x *= sin(offset); 47 | gl_FragColor = vec4(hsv2rgb(hsl), 1.); 48 | } 49 | `; 50 | 51 | function createSilkPass(camera: Camera) { 52 | const { $random } = useNuxtApp(); 53 | const effect = { 54 | uniforms: { 55 | ...baseUniforms(), 56 | offset: { value: $random.$getRandom() * 10 }, 57 | }, 58 | vertexShader: vertexShader, 59 | fragmentShader: fragmentShader, 60 | name: "SilkPass", 61 | }; 62 | const pass = new ShaderPass(effect); 63 | pass.renderToScreen = true; 64 | return pass; 65 | } 66 | 67 | export { createSilkPass }; 68 | -------------------------------------------------------------------------------- /World/systems/pass/stepped.ts: -------------------------------------------------------------------------------- 1 | import { Camera } from "three"; 2 | import { ShaderPass } from "three/examples/jsm/postprocessing/ShaderPass"; 3 | import { 4 | baseShaderUniforms, 5 | baseUniforms, 6 | translateColorspace, 7 | } from "~/World/systems/pass/helpers"; 8 | 9 | const vertexShader = ` 10 | varying vec2 vUv; 11 | void main() { 12 | vUv = uv; 13 | gl_Position = projectionMatrix 14 | * modelViewMatrix 15 | * vec4( position, 1.0 ); 16 | } 17 | `; 18 | 19 | const fragmentShader = ` 20 | uniform float offset; 21 | varying vec2 vUv; 22 | ${baseShaderUniforms} 23 | ${translateColorspace} 24 | 25 | float stepped(in float s, in float scale, in int steps) { 26 | return floor( s / ((1.0*scale) / float(steps))) * 1.0 / float(steps-1); 27 | } 28 | void main() { 29 | float o = offset / 10.; 30 | float r = o > 0.8 ? sin(offset) : o; 31 | float g = o > 0.5 ? cos(offset) : sin(offset); 32 | float b = o > 0.2 ? o : cos(offset); 33 | vec4 Color1 = normalize(vec4(r, g, b, 1.0)); 34 | vec3 hsv = rgb2hsv(normalize(vec3(r,g,b))); 35 | hsv.x *= sin((position.x + offset) / 10.); 36 | Color1 = vec4(hsv2rgb(hsv),1.); 37 | vec4 Color2 = vec4(colors.background.rgb, 1.0); 38 | int NumSteps = int(20. * o); 39 | float aspect = width / height; 40 | 41 | vec2 uv = vUv; 42 | vec2 center = vec2(0.5, 0.5 - (borders.top / (borders.bottom * 100.))); 43 | 44 | uv.x *= aspect; 45 | center.x *= aspect; 46 | float dist = distance( uv, center); 47 | float size = offset / 10. + offset * abs(sin(position.y / 10.)); 48 | float s = stepped(dist, size, NumSteps ); 49 | 50 | gl_FragColor = mix(Color1, Color2, clamp(s, 0.0, 1.0)); 51 | } 52 | `; 53 | 54 | function createSteppedPass(camera: Camera) { 55 | const { $random } = useNuxtApp(); 56 | const effect = { 57 | uniforms: { 58 | ...baseUniforms(), 59 | offset: { value: $random.$getRandom() * 10 }, 60 | }, 61 | vertexShader: vertexShader, 62 | fragmentShader: fragmentShader, 63 | name: "SteppedPass", 64 | }; 65 | const pass = new ShaderPass(effect); 66 | pass.renderToScreen = true; 67 | return pass; 68 | } 69 | 70 | export { createSteppedPass }; 71 | -------------------------------------------------------------------------------- /World/systems/pass/steppedWobble.ts: -------------------------------------------------------------------------------- 1 | import { Camera } from "three"; 2 | import { ShaderPass } from "three/examples/jsm/postprocessing/ShaderPass"; 3 | import { 4 | baseShaderUniforms, 5 | baseUniforms, 6 | translateColorspace, 7 | } from "~/World/systems/pass/helpers"; 8 | 9 | const vertexShader = ` 10 | varying vec2 vUv; 11 | void main() { 12 | vUv = uv; 13 | gl_Position = projectionMatrix 14 | * modelViewMatrix 15 | * vec4( position, 1.0 ); 16 | } 17 | `; 18 | 19 | const fragmentShader = ` 20 | uniform float offset; 21 | varying vec2 vUv; 22 | ${baseShaderUniforms} 23 | ${translateColorspace} 24 | 25 | float stepped(in float s, in float scale, in int steps) { 26 | return floor( s / ((1.0*scale) / float(steps))) * 1.0 / float(steps-1); 27 | } 28 | 29 | vec2 random2(vec2 st){ 30 | st = vec2( dot(st,vec2(127.1,311.7)), 31 | dot(st,vec2(269.5,183.3)) ); 32 | return -1.0 + 2.0*fract(sin(st)*43758.5453123); 33 | } 34 | 35 | float noise(vec2 st) { 36 | vec2 i = floor(st); 37 | vec2 f = fract(st); 38 | 39 | vec2 u = f*f*(3.0-2.0*f); 40 | 41 | return mix( mix( dot( random2(i + vec2(0.0,0.0) ), f - vec2(0.0,0.0) ), 42 | dot( random2(i + vec2(1.0,0.0) ), f - vec2(1.0,0.0) ), u.x), 43 | mix( dot( random2(i + vec2(0.0,1.0) ), f - vec2(0.0,1.0) ), 44 | dot( random2(i + vec2(1.0,1.0) ), f - vec2(1.0,1.0) ), u.x), u.y); 45 | } 46 | 47 | void main() { 48 | float o = offset / 10.; 49 | float r = o > 0.8 ? sin(offset) : o; 50 | float g = o > 0.5 ? cos(offset) : sin(offset); 51 | float b = o > 0.2 ? o : cos(offset); 52 | vec4 Color1 = normalize(vec4(r, g, b, 1.0)); 53 | vec3 hsv = rgb2hsv(normalize(vec3(r,g,b))); 54 | hsv.x *= sin((position.x + offset) / 10.); 55 | Color1 = vec4(hsv2rgb(hsv),1.); 56 | vec4 Color2 = vec4(colors.background.rgb, 1.0); 57 | int NumSteps = int(20. * o); 58 | float aspect = width / height; 59 | 60 | vec2 uv = vUv; 61 | vec2 center = vec2(0.5, 0.5 - (borders.top / (borders.bottom * 100.))); 62 | 63 | uv.x *= aspect; 64 | center.x *= aspect; 65 | float dist = distance( uv, center); 66 | float size = offset / 10. + offset * abs(sin(position.y / 10.)); 67 | size += noise(uv * 2. * offset) * 0.1; 68 | float s = stepped(dist, size, NumSteps ); 69 | 70 | gl_FragColor = mix(Color1, Color2, clamp(s, 0.0, 1.0)); 71 | } 72 | `; 73 | 74 | function createSteppedWobblePass(camera: Camera) { 75 | const { $random } = useNuxtApp(); 76 | const effect = { 77 | uniforms: { 78 | ...baseUniforms(), 79 | offset: { value: $random.$getRandom() * 10 }, 80 | }, 81 | vertexShader: vertexShader, 82 | fragmentShader: fragmentShader, 83 | name: "SteppedWobblePass", 84 | }; 85 | const pass = new ShaderPass(effect); 86 | pass.renderToScreen = true; 87 | return pass; 88 | } 89 | 90 | export { createSteppedWobblePass }; 91 | -------------------------------------------------------------------------------- /World/systems/pass/verner.ts: -------------------------------------------------------------------------------- 1 | import { ShaderPass } from "three/examples/jsm/postprocessing/ShaderPass.js"; 2 | import { Camera } from "three"; 3 | import { baseShaderUniforms, baseUniforms } from "~/World/systems/pass/helpers"; 4 | 5 | const vertexShader = ` 6 | varying vec2 vUv; 7 | void main() { 8 | vUv = uv; 9 | gl_Position = projectionMatrix 10 | * modelViewMatrix 11 | * vec4( position, 1.0 ); 12 | } 13 | `; 14 | 15 | const fragmentShader = ` 16 | uniform float offset; 17 | varying vec2 vUv; 18 | ${baseShaderUniforms} 19 | 20 | void main() { 21 | vec2 uv = vUv; 22 | vec2 p = uv; 23 | vec4 o = gl_FragColor; 24 | p /= offset; 25 | p.x *= sign(cos(length(ceil(p))*99.)); 26 | o = cos( min( length(p = fract(p)), length(--p) ) * 31.4*vec4(position.x, position.y, .6,0) ); 27 | gl_FragColor = o; 28 | } 29 | `; 30 | 31 | function createVernerPass(camera: Camera) { 32 | const { $random } = useNuxtApp(); 33 | const effect = { 34 | uniforms: { 35 | ...baseUniforms(), 36 | offset: { value: $random.$getRandom() * 10 }, 37 | }, 38 | vertexShader: vertexShader, 39 | fragmentShader: fragmentShader, 40 | name: "VernerPass", 41 | }; 42 | const pass = new ShaderPass(effect); 43 | pass.renderToScreen = true; 44 | return pass; 45 | } 46 | 47 | export { createVernerPass }; 48 | -------------------------------------------------------------------------------- /World/systems/pass/zebra.ts: -------------------------------------------------------------------------------- 1 | import { ShaderPass } from "three/examples/jsm/postprocessing/ShaderPass.js"; 2 | import { Camera } from "three"; 3 | import { baseShaderUniforms, baseUniforms } from "~/World/systems/pass/helpers"; 4 | 5 | const vertexShader = ` 6 | varying vec2 vUv; 7 | void main() { 8 | vUv = uv; 9 | gl_Position = projectionMatrix 10 | * modelViewMatrix 11 | * vec4( position, 1.0 ); 12 | } 13 | `; 14 | 15 | const fragmentShader = ` 16 | uniform float offset; 17 | varying vec2 vUv; 18 | ${baseShaderUniforms} 19 | 20 | void main() { 21 | vec2 uv = vUv; 22 | float t = (position.x * position.y) + offset; 23 | vec2 center = uv / 4.; 24 | float g = 3.1; 25 | center.x+=sin(uv.y*g+position.x); 26 | center.y+=cos(uv.x*g+position.y); 27 | float d = distance(uv,center); 28 | float k = -sin(d*6.283*10. - t); 29 | float e = smoothstep(0., fwidth(k)*1.5, k); 30 | float finalVal = sqrt(max(e, 0.)); 31 | vec3 mixed = mix(colors.color.rgb, colors.background.rgb, clamp(finalVal, 0.0, 1.0)); 32 | gl_FragColor = vec4(mixed, 1.); 33 | } 34 | `; 35 | 36 | function createZebraPass(camera: Camera) { 37 | const { $random } = useNuxtApp(); 38 | const effect = { 39 | uniforms: { 40 | ...baseUniforms(), 41 | offset: { value: $random.$getRandom() * 10 }, 42 | }, 43 | vertexShader: vertexShader, 44 | fragmentShader: fragmentShader, 45 | name: "ZebraPass", 46 | }; 47 | const pass = new ShaderPass(effect); 48 | pass.renderToScreen = true; 49 | return pass; 50 | } 51 | 52 | export { createZebraPass }; 53 | -------------------------------------------------------------------------------- /World/systems/renderer.ts: -------------------------------------------------------------------------------- 1 | import { ACESFilmicToneMapping, PCFSoftShadowMap, WebGLRenderer } from "three"; 2 | import { HEIGHT, WIDTH } from "~/constants"; 3 | 4 | function createBaseRenderer() { 5 | const renderer = new WebGLRenderer({ 6 | alpha: true, 7 | antialias: true, 8 | powerPreference: "high-performance", 9 | preserveDrawingBuffer: true, 10 | }); 11 | renderer.setClearColor(0x000000, 0); 12 | renderer.setSize(WIDTH, HEIGHT); 13 | renderer.shadowMap.enabled = true; // important! 14 | renderer.shadowMap.type = PCFSoftShadowMap; 15 | renderer.toneMapping = ACESFilmicToneMapping; 16 | renderer.toneMappingExposure = 1; 17 | return renderer; 18 | } 19 | 20 | function createRenderer() { 21 | const renderer = createBaseRenderer(); 22 | renderer.setPixelRatio(window.devicePixelRatio ? window.devicePixelRatio : 1); 23 | return renderer; 24 | } 25 | 26 | function createExportRenderer() { 27 | const renderer = createBaseRenderer(); 28 | renderer.setPixelRatio(window.devicePixelRatio ? window.devicePixelRatio : 2); 29 | return renderer; 30 | } 31 | 32 | function createLandingRenderer(width: number, height: number) { 33 | const renderer = new WebGLRenderer({ 34 | alpha: true, 35 | antialias: true, 36 | powerPreference: "high-performance", 37 | preserveDrawingBuffer: true, 38 | }); 39 | renderer.setClearColor(0x000000, 0); 40 | renderer.setSize(width, height); 41 | renderer.shadowMap.enabled = true; // important! 42 | renderer.shadowMap.type = PCFSoftShadowMap; 43 | renderer.toneMapping = ACESFilmicToneMapping; 44 | renderer.toneMappingExposure = 1; 45 | renderer.setPixelRatio(window.devicePixelRatio); 46 | return renderer; 47 | } 48 | 49 | export { createRenderer, createExportRenderer, createLandingRenderer }; 50 | -------------------------------------------------------------------------------- /World/things/camera.ts: -------------------------------------------------------------------------------- 1 | import { OrthographicCamera } from "three"; 2 | import { HEIGHT, WIDTH } from "~/constants"; 3 | 4 | function createCamera() { 5 | return new OrthographicCamera( 6 | WIDTH / -2, 7 | WIDTH / 2, 8 | HEIGHT / 2, 9 | HEIGHT / -2, 10 | 0, 11 | Number.MAX_VALUE, 12 | ); 13 | } 14 | 15 | function createCameraParams( 16 | left: number, 17 | right: number, 18 | top: number, 19 | bottom: number, 20 | ) { 21 | return new OrthographicCamera(left, right, top, bottom, 0, Number.MAX_VALUE); 22 | } 23 | 24 | export { createCamera, createCameraParams }; 25 | -------------------------------------------------------------------------------- /World/things/index.ts: -------------------------------------------------------------------------------- 1 | export { createCamera } from "./camera"; 2 | export { createScene } from "./scene"; 3 | -------------------------------------------------------------------------------- /World/things/scene.ts: -------------------------------------------------------------------------------- 1 | import { Scene } from "three"; 2 | 3 | function createScene() { 4 | return new Scene(); 5 | } 6 | 7 | export { createScene }; 8 | -------------------------------------------------------------------------------- /app.vue: -------------------------------------------------------------------------------- 1 | 7 | 12 | -------------------------------------------------------------------------------- /assets/css/reset.css: -------------------------------------------------------------------------------- 1 | *, 2 | ::before, 3 | ::after { 4 | box-sizing: border-box; 5 | /* 1 */ 6 | border-width: 0; 7 | /* 2 */ 8 | border-style: solid; 9 | /* 2 */ 10 | border-color: #e5e7eb; 11 | /* 2 */ 12 | } 13 | 14 | /* 15 | 1. Use a consistent sensible line-height in all browsers. 16 | 2. Prevent adjustments of font size after orientation changes in iOS. 17 | 3. Use a more readable tab size. 18 | 4. Use the user's configured `sans` font-family by default. 19 | 5. Use the user's configured `sans` font-feature-settings by default. 20 | */ 21 | 22 | html { 23 | line-height: 1.5; 24 | /* 1 */ 25 | -webkit-text-size-adjust: 100%; 26 | /* 2 */ 27 | -moz-tab-size: 4; 28 | /* 3 */ 29 | -o-tab-size: 4; 30 | tab-size: 4; 31 | /* 3 */ 32 | font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 33 | "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, 34 | "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; 35 | /* 4 */ 36 | font-feature-settings: normal; 37 | /* 5 */ 38 | scroll-behavior: smooth; 39 | } 40 | 41 | /* 42 | 1. Remove the margin in all browsers. 43 | 2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. 44 | */ 45 | 46 | body { 47 | margin: 0; 48 | /* 1 */ 49 | line-height: inherit; 50 | /* 2 */ 51 | scroll-behavior: smooth; 52 | } 53 | 54 | /* 55 | 1. Add the correct height in Firefox. 56 | 2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) 57 | 3. Ensure horizontal rules are visible by default. 58 | */ 59 | 60 | hr { 61 | height: 0; 62 | /* 1 */ 63 | color: inherit; 64 | /* 2 */ 65 | border-top-width: 1px; 66 | /* 3 */ 67 | } 68 | 69 | /* 70 | Add the correct text decoration in Chrome, Edge, and Safari. 71 | */ 72 | 73 | abbr:where([title]) { 74 | -webkit-text-decoration: underline dotted; 75 | text-decoration: underline dotted; 76 | } 77 | 78 | /* 79 | Remove the default font size and weight for headings. 80 | */ 81 | 82 | h1, 83 | h2, 84 | h3, 85 | h4, 86 | h5, 87 | h6 { 88 | font-size: inherit; 89 | font-weight: inherit; 90 | } 91 | 92 | /* 93 | Reset links to optimize for opt-in styling instead of opt-out. 94 | */ 95 | 96 | a { 97 | color: inherit; 98 | text-decoration: inherit; 99 | } 100 | 101 | /* 102 | Add the correct font weight in Edge and Safari. 103 | */ 104 | 105 | b, 106 | strong { 107 | font-weight: bolder; 108 | } 109 | 110 | /* 111 | 1. Use the user's configured `mono` font family by default. 112 | 2. Correct the odd `em` font sizing in all browsers. 113 | */ 114 | 115 | code, 116 | kbd, 117 | samp, 118 | pre { 119 | font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 120 | "Liberation Mono", "Courier New", monospace; 121 | /* 1 */ 122 | font-size: 1em; 123 | /* 2 */ 124 | } 125 | 126 | /* 127 | Add the correct font size in all browsers. 128 | */ 129 | 130 | small { 131 | font-size: 80%; 132 | } 133 | 134 | /* 135 | Prevent `sub` and `sup` elements from affecting the line height in all browsers. 136 | */ 137 | 138 | sub, 139 | sup { 140 | font-size: 75%; 141 | line-height: 0; 142 | position: relative; 143 | vertical-align: baseline; 144 | } 145 | 146 | sub { 147 | bottom: -0.25em; 148 | } 149 | 150 | sup { 151 | top: -0.5em; 152 | } 153 | 154 | /* 155 | 1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) 156 | 2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) 157 | 3. Remove gaps between table borders by default. 158 | */ 159 | 160 | table { 161 | text-indent: 0; 162 | /* 1 */ 163 | border-color: inherit; 164 | /* 2 */ 165 | border-collapse: collapse; 166 | /* 3 */ 167 | } 168 | 169 | /* 170 | 1. Change the font styles in all browsers. 171 | 2. Remove the margin in Firefox and Safari. 172 | 3. Remove default padding in all browsers. 173 | */ 174 | 175 | button, 176 | input, 177 | optgroup, 178 | select, 179 | textarea { 180 | font-family: inherit; 181 | /* 1 */ 182 | font-size: 100%; 183 | /* 1 */ 184 | font-weight: inherit; 185 | /* 1 */ 186 | line-height: inherit; 187 | /* 1 */ 188 | color: inherit; 189 | /* 1 */ 190 | margin: 0; 191 | /* 2 */ 192 | padding: 0; 193 | /* 3 */ 194 | } 195 | 196 | /* 197 | Remove the inheritance of text transform in Edge and Firefox. 198 | */ 199 | 200 | button, 201 | select { 202 | text-transform: none; 203 | } 204 | 205 | /* 206 | 1. Correct the inability to style clickable types in iOS and Safari. 207 | 2. Remove default button styles. 208 | */ 209 | 210 | button, 211 | [type="button"], 212 | [type="reset"], 213 | [type="submit"] { 214 | -webkit-appearance: button; 215 | /* 1 */ 216 | background-color: transparent; 217 | /* 2 */ 218 | background-image: none; 219 | /* 2 */ 220 | } 221 | 222 | /* 223 | Use the modern Firefox focus style for all focusable elements. 224 | */ 225 | 226 | :-moz-focusring { 227 | outline: auto; 228 | } 229 | 230 | /* 231 | Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) 232 | */ 233 | 234 | :-moz-ui-invalid { 235 | box-shadow: none; 236 | } 237 | 238 | /* 239 | Add the correct vertical alignment in Chrome and Firefox. 240 | */ 241 | 242 | progress { 243 | vertical-align: baseline; 244 | } 245 | 246 | /* 247 | Correct the cursor style of increment and decrement buttons in Safari. 248 | */ 249 | 250 | ::-webkit-inner-spin-button, 251 | ::-webkit-outer-spin-button { 252 | height: auto; 253 | } 254 | 255 | /* 256 | 1. Correct the odd appearance in Chrome and Safari. 257 | 2. Correct the outline style in Safari. 258 | */ 259 | 260 | [type="search"] { 261 | -webkit-appearance: textfield; 262 | /* 1 */ 263 | outline-offset: -2px; 264 | /* 2 */ 265 | } 266 | 267 | /* 268 | Remove the inner padding in Chrome and Safari on macOS. 269 | */ 270 | 271 | ::-webkit-search-decoration { 272 | -webkit-appearance: none; 273 | } 274 | 275 | /* 276 | 1. Correct the inability to style clickable types in iOS and Safari. 277 | 2. Change font properties to `inherit` in Safari. 278 | */ 279 | 280 | ::-webkit-file-upload-button { 281 | -webkit-appearance: button; 282 | /* 1 */ 283 | font: inherit; 284 | /* 2 */ 285 | } 286 | 287 | /* 288 | Add the correct display in Chrome and Safari. 289 | */ 290 | 291 | summary { 292 | display: list-item; 293 | } 294 | 295 | /* 296 | Removes the default spacing and border for appropriate elements. 297 | */ 298 | 299 | blockquote, 300 | dl, 301 | dd, 302 | h1, 303 | h2, 304 | h3, 305 | h4, 306 | h5, 307 | h6, 308 | hr, 309 | figure, 310 | p, 311 | pre { 312 | margin: 0; 313 | } 314 | 315 | fieldset { 316 | margin: 0; 317 | padding: 0; 318 | } 319 | 320 | legend { 321 | padding: 0; 322 | } 323 | 324 | ol, 325 | ul, 326 | menu { 327 | list-style: none; 328 | margin: 0; 329 | padding: 0; 330 | } 331 | 332 | /* 333 | Prevent resizing textareas horizontally by default. 334 | */ 335 | 336 | textarea { 337 | resize: vertical; 338 | } 339 | 340 | /* 341 | 1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) 342 | 2. Set the default placeholder color to the user's configured gray 400 color. 343 | */ 344 | 345 | input::-moz-placeholder, 346 | textarea::-moz-placeholder { 347 | opacity: 1; 348 | /* 1 */ 349 | color: #9ca3af; 350 | /* 2 */ 351 | } 352 | 353 | input::placeholder, 354 | textarea::placeholder { 355 | opacity: 1; 356 | /* 1 */ 357 | color: #9ca3af; 358 | /* 2 */ 359 | } 360 | 361 | /* 362 | Set the default cursor for buttons. 363 | */ 364 | 365 | button, 366 | [role="button"] { 367 | cursor: pointer; 368 | } 369 | 370 | /* 371 | Make sure disabled buttons don't get the pointer cursor. 372 | */ 373 | 374 | :disabled { 375 | cursor: default; 376 | } 377 | 378 | /* 379 | 1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) 380 | 2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) 381 | This can trigger a poorly considered lint error in some tools but is included by design. 382 | */ 383 | 384 | img, 385 | svg, 386 | video, 387 | canvas, 388 | audio, 389 | iframe, 390 | embed, 391 | object { 392 | display: block; 393 | /* 1 */ 394 | vertical-align: middle; 395 | /* 2 */ 396 | } 397 | 398 | /* 399 | Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) 400 | */ 401 | 402 | img, 403 | video { 404 | max-width: 100%; 405 | height: auto; 406 | } 407 | 408 | /* Make elements with the HTML hidden attribute stay hidden by default */ 409 | 410 | [hidden] { 411 | display: none; 412 | } -------------------------------------------------------------------------------- /assets/scss/global.scss: -------------------------------------------------------------------------------- 1 | html { 2 | font-size: 15.11px; 3 | 4 | @media(min-width: $small) { 5 | font-size: 17px 6 | } 7 | 8 | @media(min-width: $medium) { 9 | font-size: 21.5px 10 | } 11 | 12 | @media(min-width: $large) { 13 | font-size: 23.5px 14 | } 15 | 16 | @media(min-width: $huge) { 17 | font-size: 25.5px; 18 | } 19 | } 20 | 21 | body { 22 | margin: 59px auto 0; 23 | background: $base; 24 | color: $text; 25 | font-family: Arimo, sans-serif; 26 | min-height: 100vh; 27 | } 28 | -------------------------------------------------------------------------------- /assets/scss/variables.scss: -------------------------------------------------------------------------------- 1 | $base: #F8F7F4; 2 | $text: #3111B9; 3 | $active: #FF6E50; 4 | $text-active: #fff; 5 | 6 | $huge: 1600px; 7 | $large: 1050px; 8 | $medium: 769px; 9 | $small: 420px; 10 | 11 | @mixin level-extra { 12 | font-family: "Libre Baskerville", serif; 13 | font-size: 7.33rem; 14 | font-style: normal; 15 | font-weight: 700; 16 | letter-spacing: 0.64px; 17 | } 18 | 19 | @mixin level1 { 20 | font-family: "Libre Baskerville", serif; 21 | font-size: 4.235rem; 22 | font-style: normal; 23 | font-weight: 700; 24 | letter-spacing: 0.64px; 25 | } 26 | 27 | @mixin level2 { 28 | font-size: 2.61765rem; 29 | font-style: normal; 30 | line-height: 132.5%; 31 | font-weight: 700; 32 | } 33 | 34 | @mixin level3 { 35 | font-size: 1.618rem; 36 | font-style: normal; 37 | line-height: 135%; 38 | font-weight: 400; 39 | } 40 | 41 | @mixin level4 { 42 | font-size: 0.618rem; 43 | font-style: normal; 44 | line-height: 135%; 45 | font-weight: 400; 46 | } 47 | 48 | @mixin code { 49 | font-style: normal; 50 | line-height: 135%; 51 | font-weight: 400; 52 | 53 | pre { 54 | background-color: #D1D1D0; 55 | overflow: auto; 56 | font-family: 'Monaco', monospace; 57 | padding: 0 1em; 58 | } 59 | 60 | code { 61 | font-family: 'Monaco', monospace; 62 | font-size: 0.618rem; 63 | line-height: 100%; 64 | background-color: #eee; 65 | padding: 0.2em; 66 | letter-spacing: -0.05em; 67 | word-break: normal; 68 | border-radius: 5px; 69 | } 70 | 71 | pre code { 72 | border: none; 73 | background: none; 74 | font-size: 0.618rem * 0.875; 75 | line-height: 1em; 76 | letter-spacing: normal; 77 | word-break: break-all; 78 | } 79 | } -------------------------------------------------------------------------------- /components/AppHeader.vue: -------------------------------------------------------------------------------- 1 | 15 | 19 | 66 | -------------------------------------------------------------------------------- /components/Footer.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 56 | -------------------------------------------------------------------------------- /components/ToolLayout.vue: -------------------------------------------------------------------------------- 1 | 20 | 26 | 87 | -------------------------------------------------------------------------------- /components/glass/GlassControls.vue: -------------------------------------------------------------------------------- 1 | 115 | 206 | 297 | -------------------------------------------------------------------------------- /components/glass/GlassCss.vue: -------------------------------------------------------------------------------- 1 | 26 | 114 | 157 | -------------------------------------------------------------------------------- /components/glass/GlassImage.vue: -------------------------------------------------------------------------------- 1 | 20 | 88 | 124 | -------------------------------------------------------------------------------- /components/glass/GlassImageSegment.vue: -------------------------------------------------------------------------------- 1 | 4 | 17 | 41 | -------------------------------------------------------------------------------- /components/glass/GlassWrapper.vue: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /components/landing/Hero.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 29 | 147 | -------------------------------------------------------------------------------- /components/landing/Tool.vue: -------------------------------------------------------------------------------- 1 | 25 | 41 | 113 | -------------------------------------------------------------------------------- /components/landing/Tools.vue: -------------------------------------------------------------------------------- 1 | 20 | 23 | 31 | -------------------------------------------------------------------------------- /components/poster/AppContainer.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 42 | 43 | 70 | -------------------------------------------------------------------------------- /components/poster/controls/ColorPicker.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 70 | 71 | 137 | -------------------------------------------------------------------------------- /components/poster/controls/ColorsPicker.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 53 | 54 | 60 | -------------------------------------------------------------------------------- /components/poster/controls/Controls.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 19 | 20 | 50 | -------------------------------------------------------------------------------- /components/poster/controls/ExportOptions.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 24 | 25 | 72 | -------------------------------------------------------------------------------- /components/poster/scene/Scene.vue: -------------------------------------------------------------------------------- 1 | 14 | 273 | 280 | 290 | -------------------------------------------------------------------------------- /components/poster/scene/SceneText.vue: -------------------------------------------------------------------------------- 1 | 6 | 13 | 20 | -------------------------------------------------------------------------------- /components/poster/scene/text/TextTitle.vue: -------------------------------------------------------------------------------- 1 |