├── .prettierrc.json ├── .eslintignore ├── World ├── systems │ ├── index.ts │ ├── pass │ │ ├── verner.ts │ │ ├── masking.ts │ │ ├── grain.ts │ │ ├── index.ts │ │ ├── zebra.ts │ │ ├── gradient.ts │ │ ├── chess.ts │ │ ├── silk.ts │ │ ├── dotsGrid.ts │ │ ├── liquid.ts │ │ ├── liquidLarge.ts │ │ ├── dotsLanding.ts │ │ ├── helpers.ts │ │ ├── stepped.ts │ │ └── steppedWobble.ts │ ├── composer.ts │ └── renderer.ts └── things │ ├── index.ts │ ├── scene.ts │ └── camera.ts ├── public ├── star.png ├── favicon.ico ├── backdrop.png ├── favicon-16x16.png ├── favicon-32x32.png ├── apple-touch-icon.png ├── android-chrome-192x192.png └── android-chrome-512x512.png ├── tsconfig.json ├── constants └── index.ts ├── app.vue ├── pages ├── glass │ └── index.vue ├── progressive-blur │ └── index.vue ├── poster │ ├── index.vue │ └── download.vue ├── index.vue └── about.vue ├── layouts ├── tool.vue └── default.vue ├── plugins ├── useEmitter.ts └── useRandom.ts ├── components ├── glass │ ├── GlassWrapper.vue │ ├── GlassImageSegment.vue │ ├── GlassImage.vue │ ├── GlassCss.vue │ └── GlassControls.vue ├── poster │ ├── scene │ │ ├── SceneText.vue │ │ ├── text │ │ │ └── TextTitle.vue │ │ └── Scene.vue │ ├── controls │ │ ├── Controls.vue │ │ ├── ExportOptions.vue │ │ ├── ColorsPicker.vue │ │ └── ColorPicker.vue │ └── AppContainer.vue ├── landing │ ├── Tools.vue │ ├── Tool.vue │ └── Hero.vue ├── Footer.vue ├── AppHeader.vue ├── progressive-blur │ ├── ProgressiveBlur2.vue │ ├── BlurredImage.vue │ ├── ProgressiveBlur.vue │ ├── MainWrapper.vue │ ├── CSSDisplay.vue │ └── Controls.vue └── ToolLayout.vue ├── helpers ├── fonts.ts ├── glass │ ├── images.ts │ └── index.ts └── progressive-blur │ ├── images.ts │ └── index.ts ├── .eslintrc.json ├── assets ├── scss │ ├── global.scss │ └── variables.scss └── css │ └── reset.css ├── README.md ├── store └── scene.ts ├── package.json ├── .gitignore └── nuxt.config.ts /.prettierrc.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | .nuxt 2 | dist 3 | .output 4 | .vercel -------------------------------------------------------------------------------- /World/systems/index.ts: -------------------------------------------------------------------------------- 1 | export { createRenderer } from "./renderer"; 2 | -------------------------------------------------------------------------------- /public/star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devslovecoffee/PosterRamen/HEAD/public/star.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devslovecoffee/PosterRamen/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/backdrop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devslovecoffee/PosterRamen/HEAD/public/backdrop.png -------------------------------------------------------------------------------- /World/things/index.ts: -------------------------------------------------------------------------------- 1 | export { createCamera } from "./camera"; 2 | export { createScene } from "./scene"; 3 | -------------------------------------------------------------------------------- /public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devslovecoffee/PosterRamen/HEAD/public/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devslovecoffee/PosterRamen/HEAD/public/favicon-32x32.png -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devslovecoffee/PosterRamen/HEAD/public/apple-touch-icon.png -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // https://v3.nuxtjs.org/concepts/typescript 3 | "extends": "./.nuxt/tsconfig.json" 4 | } 5 | -------------------------------------------------------------------------------- /public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devslovecoffee/PosterRamen/HEAD/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devslovecoffee/PosterRamen/HEAD/public/android-chrome-512x512.png -------------------------------------------------------------------------------- /World/things/scene.ts: -------------------------------------------------------------------------------- 1 | import { Scene } from "three"; 2 | 3 | function createScene() { 4 | return new Scene(); 5 | } 6 | 7 | export { createScene }; 8 | -------------------------------------------------------------------------------- /constants/index.ts: -------------------------------------------------------------------------------- 1 | export const WIDTH: number = 400; 2 | export const HEIGHT: number = 600; 3 | 4 | export const TITLE_DEFAULT: string = "Edit Text"; 5 | -------------------------------------------------------------------------------- /app.vue: -------------------------------------------------------------------------------- 1 | 7 | 12 | -------------------------------------------------------------------------------- /pages/glass/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 11 | -------------------------------------------------------------------------------- /layouts/tool.vue: -------------------------------------------------------------------------------- 1 | 6 | 10 | -------------------------------------------------------------------------------- /layouts/default.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 11 | -------------------------------------------------------------------------------- /pages/progressive-blur/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 11 | -------------------------------------------------------------------------------- /pages/poster/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /plugins/useEmitter.ts: -------------------------------------------------------------------------------- 1 | import { defineNuxtPlugin } from "#app"; 2 | import mitt from "mitt"; 3 | const emitter = mitt(); 4 | 5 | export default defineNuxtPlugin((nuxtApp) => { 6 | nuxtApp.provide("bus", { 7 | $on: emitter.on, 8 | $emit: emitter.emit, 9 | $off: emitter.off, 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /components/glass/GlassWrapper.vue: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /helpers/fonts.ts: -------------------------------------------------------------------------------- 1 | const fonts = [ 2 | "Goldman", 3 | "Inter", 4 | "Press Start 2P", 5 | "Righteous", 6 | "Space Grotesk", 7 | "Bellota Text", 8 | ]; 9 | 10 | // function sampleFont() { 11 | // const { $random } = useNuxtApp(); 12 | // const index = Math.floor($random.$getRandom() * fonts.length); 13 | // return fonts[index]; 14 | // } 15 | // sampleFont 16 | export { fonts }; 17 | -------------------------------------------------------------------------------- /pages/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 14 | 19 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /components/poster/scene/SceneText.vue: -------------------------------------------------------------------------------- 1 | 6 | 13 | 20 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /plugins/useRandom.ts: -------------------------------------------------------------------------------- 1 | function splitmix32(a: number) { 2 | return function () { 3 | a |= 0; 4 | a = (a + 0x9e3779b9) | 0; 5 | let t = a ^ (a >>> 16); 6 | t = Math.imul(t, 0x21f0aaad); 7 | t = t ^ (t >>> 15); 8 | t = Math.imul(t, 0x735a2d97); 9 | return ((t = t ^ (t >>> 15)) >>> 0) / 4294967296; 10 | }; 11 | } 12 | export default defineNuxtPlugin((nuxtApp) => { 13 | const rng = ref<() => number>(() => -1); 14 | nuxtApp.provide("random", { 15 | $setSeed: (providedSeed: number) => { 16 | rng.value = splitmix32(providedSeed); 17 | }, 18 | $getRandom: () => rng.value(), 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /components/landing/Tools.vue: -------------------------------------------------------------------------------- 1 | 20 | 23 | 31 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /pages/about.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 45 | -------------------------------------------------------------------------------- /components/glass/GlassImageSegment.vue: -------------------------------------------------------------------------------- 1 | 4 | 17 | 41 | -------------------------------------------------------------------------------- /components/poster/controls/Controls.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 19 | 20 | 50 | -------------------------------------------------------------------------------- /store/scene.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from "pinia"; 2 | import { Vector4 } from "three"; 3 | 4 | export interface StoredScene { 5 | id: string; 6 | seed: number; 7 | cameraX: number; 8 | cameraY: number; 9 | title: string; 10 | subtitle: string; 11 | font: string; 12 | fontColor: string; 13 | textAlign: "left" | "center" | "right"; 14 | verticalFlow: "column" | "column-reverse"; 15 | horizontalFlow: "row" | "row-reverse"; 16 | background: Vector4; 17 | color: Vector4; 18 | showBorders: boolean; 19 | showText: boolean; 20 | fullExportString?: string; 21 | exportLayers?: string[]; 22 | } 23 | export const useSceneStore = defineStore({ 24 | id: "scene-store", 25 | state: (): { 26 | scenes: Record; 27 | activeSceneId: string | null; 28 | } => { 29 | return { 30 | scenes: {}, 31 | activeSceneId: null, 32 | }; 33 | }, 34 | actions: { 35 | storeScene(data: Partial) { 36 | const scene = this.scenes[data.id!] ?? {}; 37 | this.scenes[data.id!] = { ...scene, ...data }; 38 | }, 39 | setActiveScene(id: string | null) { 40 | this.activeSceneId = id; 41 | }, 42 | }, 43 | getters: { 44 | scene: (state) => (id: string) => state.scenes[id] ?? {}, 45 | activeScene: (state) => state.activeSceneId, 46 | }, 47 | }); 48 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "build": "nuxt build", 5 | "dev": "nuxt dev", 6 | "generate": "nuxt generate", 7 | "preview": "nuxt preview", 8 | "postinstall": "nuxt prepare" 9 | }, 10 | "devDependencies": { 11 | "@nuxtjs/eslint-module": "^4.1.0", 12 | "@nuxtjs/google-fonts": "^3.0.2", 13 | "@types/file-saver": "^2.0.7", 14 | "@typescript-eslint/parser": "^6.3.0", 15 | "eslint": "^8.47.0", 16 | "eslint-plugin-vue": "^9.17.0", 17 | "nuxt": "^3.19.0", 18 | "nuxt-icon": "^0.5.0", 19 | "nuxt-seo-kit": "^1.3.9", 20 | "prettier": "3.0.1", 21 | "typescript": "^5.1.6" 22 | }, 23 | "dependencies": { 24 | "@nuxtjs/plausible": "^0.2.1", 25 | "@pinia/nuxt": "^0.4.11", 26 | "@types/three": "^0.155.0", 27 | "canvg": "^4.0.1", 28 | "dom-to-image": "^2.6.0", 29 | "draggable-resizable-vue3": "^1.0.94-beta", 30 | "file-saver": "^2.0.5", 31 | "html2canvas": "^1.4.1", 32 | "js-beautify": "^1.15.1", 33 | "jspdf": "^2.5.1", 34 | "mitt": "^3.0.1", 35 | "pinia": "^2.1.6", 36 | "randomcolor": "^0.6.2", 37 | "sass": "^1.64.2", 38 | "three": "^0.155.0", 39 | "uuid": "^9.0.0", 40 | "vite": "2.6.4", 41 | "vue-3-slider-component": "^0.1.4", 42 | "vue3-colorpicker": "^2.2.3" 43 | }, 44 | "resolutions": { 45 | "string-width": "4.2.3" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /components/Footer.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 56 | -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /components/AppHeader.vue: -------------------------------------------------------------------------------- 1 | 15 | 19 | 66 | -------------------------------------------------------------------------------- /components/poster/controls/ExportOptions.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 24 | 25 | 72 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /nuxt.config.ts: -------------------------------------------------------------------------------- 1 | const fonts = { 2 | families: { 3 | Arimo: [400, 500, 600, 700], 4 | "Noto Sans": [100, 200, 300, 400, 500, 600, 700, 800, 900], 5 | "Libre Baskerville": [400, 700], 6 | }, 7 | subsets: ["latin"], 8 | display: "swap", 9 | prefetch: false, 10 | preconnect: false, 11 | preload: false, 12 | download: true, 13 | base64: false, 14 | }; 15 | export default defineNuxtConfig({ 16 | extends: ["nuxt-seo-kit"], 17 | ssr: false, 18 | nitro: { 19 | preset: "vercel-edge", 20 | }, 21 | css: ["@/assets/css/reset.css", "@/assets/scss/global.scss"], 22 | vite: { 23 | css: { 24 | preprocessorOptions: { 25 | scss: { 26 | additionalData: '@import "@/assets/scss/variables.scss";', 27 | }, 28 | }, 29 | }, 30 | }, 31 | modules: [ 32 | ["@nuxtjs/google-fonts", fonts], 33 | "@nuxtjs/eslint-module", 34 | "@pinia/nuxt", 35 | "@nuxtjs/plausible", 36 | "nuxt-icon", 37 | ], 38 | plugins: [], 39 | runtimeConfig: { 40 | public: { 41 | baseUrl: process.env.BASE_URL || "http://localhost:3000", 42 | siteUrl: process.env.NUXT_PUBLIC_SITE_URL || "localhost:3000", 43 | siteName: "Poster Ramen", 44 | siteDescription: 45 | "Poster Ramen is a set of single-purpose tools helping you quickly prototype and explore designs & typography.", 46 | language: "en", 47 | }, 48 | }, 49 | linkChecker: { 50 | failOn404: true, 51 | }, 52 | plausible: { 53 | domain: "posterramen.com", 54 | }, 55 | sitemap: { 56 | hostname: "https://posterramen.com", 57 | gzip: true, 58 | exclude: ["/poster/download"], 59 | }, 60 | robots: { 61 | UserAgent: "*", 62 | Disallow: ["/poster/download"], 63 | }, 64 | }); 65 | -------------------------------------------------------------------------------- /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/poster/controls/ColorsPicker.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 53 | 54 | 60 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /components/progressive-blur/ProgressiveBlur2.vue: -------------------------------------------------------------------------------- 1 | 11 | 49 | 67 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /components/progressive-blur/BlurredImage.vue: -------------------------------------------------------------------------------- 1 | 10 | 38 | 69 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /components/poster/AppContainer.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 42 | 43 | 70 | -------------------------------------------------------------------------------- /components/ToolLayout.vue: -------------------------------------------------------------------------------- 1 | 20 | 26 | 87 | -------------------------------------------------------------------------------- /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/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/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/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 | -------------------------------------------------------------------------------- /helpers/glass/images.ts: -------------------------------------------------------------------------------- 1 | const glassImages = [ 2 | { 3 | id: 1, 4 | src: "https://framerusercontent.com/images/KzgDLZX8EmIvA8sZnFZhSAdagY.png", 5 | credits: `Photo by Alex Aperios on Snapshift`, 6 | alt: "Photo by Alex Aperios on Snapshift", 7 | }, 8 | { 9 | id: 2, 10 | src: "https://framerusercontent.com/images/IT9uLa9XLhCPlcUfDYCeIXLuqk.png?scale-down-to=1024", 11 | credits: `Photo by Alex Aperios on Snapshift`, 12 | alt: "Photo by Alex Aperios on Snapshift", 13 | }, 14 | { 15 | id: 3, 16 | src: "https://framerusercontent.com/images/XNGg7w0Mpawjy3xPPNl2V5Si7iI.png?scale-down-to=1024", 17 | credits: `Photo by Alex Aperios on Snapshift`, 18 | alt: "Photo by Alex Aperios on Snapshift", 19 | }, 20 | { 21 | id: 4, 22 | src: "https://framerusercontent.com/images/2pSTIybaqt6HcUx8hiW7r5mSkM.png?scale-down-to=1024", 23 | credits: `Photo by Alex Aperios on Snapshift`, 24 | alt: "Photo by Alex Aperios on Snapshift", 25 | }, 26 | { 27 | id: 5, 28 | src: "https://framerusercontent.com/images/GUQ2BjPUcUpVAvZ0MJwnVNmHQI.png?scale-down-to=1024", 29 | credits: `Photo by Alex Aperios on Snapshift`, 30 | alt: "Photo by Alex Aperios on Snapshift", 31 | }, 32 | { 33 | id: 6, 34 | src: "https://framerusercontent.com/images/MQxP1uP0Xww6UV4pX3TlAxpyVvg.png?scale-down-to=1024", 35 | credits: `Photo by Alex Aperios on Snapshift`, 36 | alt: "Photo by Alex Aperios on Snapshift", 37 | }, 38 | ]; 39 | 40 | export { glassImages }; 41 | -------------------------------------------------------------------------------- /components/progressive-blur/ProgressiveBlur.vue: -------------------------------------------------------------------------------- 1 | 11 | 66 | 84 | -------------------------------------------------------------------------------- /components/landing/Tool.vue: -------------------------------------------------------------------------------- 1 | 25 | 41 | 113 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /components/progressive-blur/MainWrapper.vue: -------------------------------------------------------------------------------- 1 | 28 | 53 | 115 | -------------------------------------------------------------------------------- /helpers/progressive-blur/images.ts: -------------------------------------------------------------------------------- 1 | const defaultImages = [ 2 | { 3 | id: 1, 4 | src: "https://images.unsplash.com/photo-1448375240586-882707db888b?q=80&w=2670&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", 5 | credits: `Photo by Sebastian Unrau on Unsplash`, 6 | alt: "Photo by Sebastian Unrau on Unsplash", 7 | }, 8 | { 9 | id: 2, 10 | src: "https://images.unsplash.com/photo-1476231682828-37e571bc172f?q=80&w=2574&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", 11 | credits: `Photo by Geranimo on Unsplash`, 12 | alt: "Photo by Geranimo on Unsplash", 13 | }, 14 | { 15 | id: 3, 16 | src: "https://images.unsplash.com/photo-1477959858617-67f85cf4f1df?q=80&w=2488&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", 17 | credits: `Photo by Pedro Lastra on Unsplash`, 18 | alt: "Photo by Pedro Lastra on Unsplash", 19 | }, 20 | { 21 | id: 4, 22 | src: "https://images.unsplash.com/photo-1700045350817-87116e8f2b3f?q=80&w=2572&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", 23 | credits: `Photo by Luke Miller on Unsplash`, 24 | alt: "Photo by Luke Miller on Unsplash", 25 | }, 26 | { 27 | id: 5, 28 | src: "https://images.unsplash.com/photo-1637560701868-b9aabf2c8fce?q=80&w=2642&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", 29 | credits: `Photo by Edward on Unsplash`, 30 | alt: "Photo by Edward on Unsplash", 31 | }, 32 | ]; 33 | 34 | export { defaultImages }; 35 | -------------------------------------------------------------------------------- /components/poster/controls/ColorPicker.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 70 | 71 | 137 | -------------------------------------------------------------------------------- /pages/poster/download.vue: -------------------------------------------------------------------------------- 1 | 26 | 99 | 100 | 147 | -------------------------------------------------------------------------------- /components/landing/Hero.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 29 | 147 | -------------------------------------------------------------------------------- /components/poster/scene/text/TextTitle.vue: -------------------------------------------------------------------------------- 1 |