{ "v": 1, "slug": "yosemite-warp-text", "name": "Carved by water", "description": "An oversized moving title meets the scale of Yosemite Falls.", "composition": { "v": 1, "format": { "aspectRatio": "16:9", "fps": 30, "resolution": "fhd" }, "tracks": [ { "id": "upper-falls-title", "kind": "overlay", "name": "CueFrame WarpText", "contents": [ { "id": "upper-falls-warp-title", "kind": "overlay", "startTime": 0, "duration": 8, "source": { "kind": "component", "componentId": "yosemite-warp-title", "props": { "text": "{{text}}", "color": "{{color}}" }, "zPlane": "front" } } ] }, { "id": "upper-falls-video", "kind": "video", "name": "Upper Falls — source", "contents": [ { "id": "upper-falls-main", "kind": "video", "startTime": 0, "duration": 8, "fit": "cover", "source": { "kind": "media", "mediaId": "{{source}}", "trim": { "start": 4, "end": 12 }, "muted": false, "volumeDb": -3, "fadeInMs": 350, "fadeOutMs": 600, "audioRole": "ambience" }, "effects": [ { "primitiveId": "color-grade", "params": { "preset": "cinematic", "temperature": 4, "intensity": 0.74 } } ] } ] } ] }, "parameters": [ { "name": "source", "type": "media", "kind": "video", "required": true, "label": "Waterfall footage", "sample": { "url": "https://cdn.cueframe.ai/templates/yosemite-warp-text/yosemite-upper-falls-sample.mp4", "sha256": "adc93b5a139ec1a7d2ba6cbd8c2e5a0b58e7d7a6fb55ae8006ef9e7452b19d45", "mimeType": "video/mp4", "durationSec": 16.016 } }, { "name": "text", "type": "string", "required": true, "default": "CARVED BY\nWATER", "maxLength": 40, "label": "Title" }, { "name": "color", "type": "color", "required": true, "default": "#eba61f", "label": "Title color" } ], "components": { "yosemite-warp-title": { "source": { "version": 2, "componentId": "yosemite-warp-title", "tsxSource": "import React, { useLayoutEffect, useRef } from 'react';\nimport { AbsoluteFill, interpolate, useCurrentFrame, useVideoConfig } from 'remotion';\n\nconst vertex = `#version 300 es\nin vec2 position;\nin vec2 uv;\nout vec2 vUv;\nvoid main() {\n vUv = uv;\n gl_Position = vec4(position, 0.0, 1.0);\n}`;\n\n// React Bits WarpText fragment shader, kept intact apart from uOpacity so the\n// component can enter and leave on the composition timeline deterministically.\nconst fragment = `#version 300 es\nprecision highp float;\n\nuniform sampler2D uTextTexture;\nuniform vec2 uResolution;\nuniform vec2 uPointer;\nuniform float uPointerActive;\nuniform float uTime;\nuniform float uWarpStrength;\nuniform float uWarpScale;\nuniform float uSpeed;\nuniform float uPointerInfluence;\nuniform float uPointerStrength;\nuniform float uRefraction;\nuniform float uRipple;\nuniform float uMotion;\nuniform float uOpacity;\n\nin vec2 vUv;\nout vec4 fragColor;\n\nfloat hash(vec2 p) {\n p = fract(p * vec2(123.34, 456.21));\n p += dot(p, p + 45.32);\n return fract(p.x * p.y);\n}\n\nfloat noise(vec2 p) {\n vec2 i = floor(p);\n vec2 f = fract(p);\n vec2 u = f * f * (3.0 - 2.0 * f);\n float a = hash(i);\n float b = hash(i + vec2(1.0, 0.0));\n float c = hash(i + vec2(0.0, 1.0));\n float d = hash(i + vec2(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\n\nfloat fbm(vec2 p) {\n float value = 0.0;\n float amplitude = 0.5;\n for (int i = 0; i < 4; i++) {\n value += amplitude * noise(p);\n p *= 2.02;\n amplitude *= 0.5;\n }\n return value;\n}\n\nvec4 sampleText(vec2 uv) {\n if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) return vec4(0.0);\n return texture(uTextTexture, uv);\n}\n\nvoid main() {\n vec2 uv = vUv;\n float aspect = uResolution.x / max(uResolution.y, 1.0);\n float time = uTime * uSpeed;\n float scale = max(uWarpScale, 0.001);\n\n vec2 drift = vec2(time * 0.055, -time * 0.045);\n float n1 = fbm(uv * scale * 3.1 + drift);\n float n2 = fbm((uv + 19.17) * scale * 3.4 - drift.yx);\n vec2 ambient = (vec2(n1, n2) - 0.5) * uWarpStrength * 0.045 * uMotion;\n\n vec2 pointerDelta = uv - uPointer;\n vec2 aspectDelta = vec2(pointerDelta.x * aspect, pointerDelta.y);\n float dist = length(aspectDelta);\n float radius = max(uPointerInfluence, 0.001);\n float t = clamp(dist / radius, 0.0, 1.0);\n float lens = smoothstep(radius, 0.0, dist) * uPointerActive;\n float bulge = t * (1.0 - t) * (1.0 - t) * 6.75 * uPointerActive;\n vec2 dir = dist > 0.0001 ? vec2(aspectDelta.x / aspect, aspectDelta.y) / dist : vec2(0.0);\n\n float rippleWave = sin(dist * 28.0 - time * 4.2) * 0.5 + 0.5;\n float rippleRing = (rippleWave - 0.5) * uRipple;\n vec2 pointerWarp = -dir * bulge * uPointerStrength * 0.045;\n pointerWarp += dir * rippleRing * bulge * uPointerStrength * 0.016;\n\n vec2 displaced = uv + ambient + pointerWarp;\n vec2 splitDir = ambient + pointerWarp;\n float splitLen = length(splitDir);\n splitDir = splitLen > 0.00001 ? splitDir / splitLen : vec2(0.7071, 0.7071);\n vec2 split = splitDir * uRefraction * 0.16 * (0.35 + lens * 1.65);\n\n vec4 base = sampleText(displaced);\n float r = sampleText(displaced + split).r;\n float g = base.g;\n float b = sampleText(displaced - split).b;\n float a = max(max(sampleText(displaced + split).a, base.a), sampleText(displaced - split).a);\n vec3 color = vec3(r, g, b) + lens * base.a * 0.055;\n fragColor = vec4(color, a * uOpacity);\n}`;\n\ntype Runtime = {\n gl: WebGL2RenderingContext;\n program: WebGLProgram;\n uniform: (name: string) => WebGLUniformLocation | null;\n};\n\nconst clamp = (value: number) => Math.max(0, Math.min(1, value));\nconst smooth = (value: number) => {\n const t = clamp(value);\n return t * t * (3 - 2 * t);\n};\n\nfunction CueFrameLogo({ opacity }: { opacity: number }) {\n return (\n