Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 0 additions & 15 deletions LandingPage/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 1 addition & 18 deletions LandingPage/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,17 @@
import { BrowserRouter, Routes, Route } from 'react-router-dom';
// import Lenis from '@studio-freight/lenis';
import Landing from '../src/Pages/Landing';
import PrivacyPolicy from './Pages/Privacy';
import TermsOfService from './Pages/Legal';

function App() {
// useEffect(() => {
// const lenis = new Lenis({
// duration: 1.2,
// easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
// smoothWheel: true,
// });

// function raf(time: number) {
// lenis.raf(time);
// requestAnimationFrame(raf);
// }

// requestAnimationFrame(raf);
// }, []);


return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Landing />} />
<Route path="/privacy-policy" element={<PrivacyPolicy />} />
<Route path="/terms-of-service" element={<TermsOfService />} />
{/* <Route path="*" element={<Landing />} /> */}
</Routes>
{/* <Toaster position="top-right" /> */}
</BrowserRouter>
);
}
Expand Down
196 changes: 100 additions & 96 deletions LandingPage/src/components/bg.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React, { useEffect, useRef } from "react";
import { Renderer, Program, Mesh, Triangle, Color } from "ogl";
import { Renderer, Program, Mesh, Plane } from "ogl";

interface ThreadsProps {
interface ThreadsProps extends React.ComponentPropsWithoutRef<"div"> {
color?: [number, number, number];
amplitude?: number;
distance?: number;
enableMouseInteraction?: boolean;
maxDevicePixelRatio?: number;
}

const vertexShader = `
Expand All @@ -29,36 +30,31 @@ uniform float uDistance;
uniform vec2 uMouse;

#define PI 3.1415926538

const int u_line_count = 40;
const float u_line_width = 7.0;
const float u_line_blur = 10.0;

float Perlin2D(vec2 P) {
vec2 Pi = floor(P);
vec4 Pf_Pfmin1 = P.xyxy - vec4(Pi, Pi + 1.0);
vec4 Pt = vec4(Pi.xy, Pi.xy + 1.0);
Pt = Pt - floor(Pt * (1.0 / 71.0)) * 71.0;
Pt += vec2(26.0, 161.0).xyxy;
Pt *= Pt;
Pt = Pt.xzxz * Pt.yyww;
vec4 hash_x = fract(Pt * (1.0 / 951.135664));
vec4 hash_y = fract(Pt * (1.0 / 642.949883));
vec4 grad_x = hash_x - 0.49999;
vec4 grad_y = hash_y - 0.49999;
vec4 grad_results = inversesqrt(grad_x * grad_x + grad_y * grad_y)
* (grad_x * Pf_Pfmin1.xzxz + grad_y * Pf_Pfmin1.yyww);
grad_results *= 1.4142135623730950;
vec2 blend = Pf_Pfmin1.xy * Pf_Pfmin1.xy * Pf_Pfmin1.xy
* (Pf_Pfmin1.xy * (Pf_Pfmin1.xy * 6.0 - 15.0) + 10.0);
vec4 blend2 = vec4(blend, vec2(1.0 - blend));
return dot(grad_results, blend2.zxzx * blend2.wwyy);
}
const int U_LINE_COUNT = 20;
const float U_LINE_WIDTH = 7.0;
const float U_LINE_BLUR = 10.0;

float pixel(float count, vec2 resolution) {
return (1.0 / max(resolution.x, resolution.y)) * count;
}

float hash21(vec2 p){
p = fract(p * vec2(123.34, 456.21));
p += dot(p, p + 45.32);
return fract(p.x * p.y);
}

float fastNoise(vec2 p) {
vec2 i = floor(p);
vec2 f = fract(p);
float a = hash21(i + vec2(0.0,0.0));
float b = hash21(i + vec2(1.0,0.0));
float c = hash21(i + vec2(0.0,1.0));
float d = hash21(i + vec2(1.0,1.0));
vec2 u = f*f*(3.0-2.0*f);
return mix(a, b, u.x) + (c - a)*u.y*(1.0 - u.x) + (d - b)*u.x*u.y;
}

float lineFn(vec2 st, float width, float perc, float offset, vec2 mouse, float time, float amplitude, float distance) {
float split_offset = (perc * 0.4);
float split_point = 0.1 + split_offset;
Expand All @@ -72,22 +68,22 @@ float lineFn(vec2 st, float width, float perc, float offset, vec2 mouse, float t
float blur = smoothstep(split_point, split_point + 0.05, st.x) * perc;

float xnoise = mix(
Perlin2D(vec2(time_scaled, st.x + perc) * 2.5),
Perlin2D(vec2(time_scaled, st.x + time_scaled) * 3.5) / 1.5,
fastNoise(vec2(time_scaled, st.x + perc) * 2.5),
fastNoise(vec2(time_scaled, st.x + time_scaled) * 3.5) / 1.5,
st.x * 0.3
);

float y = 0.5 + (perc - 0.5) * distance + xnoise / 2.0 * finalAmplitude;
float y = 0.5 + (perc - 0.5) * distance + (xnoise - 0.5) * finalAmplitude;

float line_start = smoothstep(
y + (width / 2.0) + (u_line_blur * pixel(1.0, iResolution.xy) * blur),
y + (width / 2.0) + (U_LINE_BLUR * pixel(1.0, iResolution.xy) * blur),
y,
st.y
);

float line_end = smoothstep(
y,
y - (width / 2.0) - (u_line_blur * pixel(1.0, iResolution.xy) * blur),
y - (width / 2.0) - (U_LINE_BLUR * pixel(1.0, iResolution.xy) * blur),
st.y
);

Expand All @@ -102,18 +98,22 @@ void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;

float line_strength = 1.0;
for (int i = 0; i < u_line_count; i++) {
float p = float(i) / float(u_line_count);
for (int i = 0; i < U_LINE_COUNT; i++) {
float p = float(i) / float(U_LINE_COUNT);
line_strength *= (1.0 - lineFn(
uv,
u_line_width * pixel(1.0, iResolution.xy) * (1.0 - p),
U_LINE_WIDTH * pixel(1.0, iResolution.xy) * (1.0 - p),
p,
(PI * 1.0) * p,
uMouse,
iTime,
uAmplitude,
uDistance
));

if (line_strength < 0.001) {
break;
}
}

float colorVal = 1.0 - line_strength;
Expand All @@ -130,105 +130,109 @@ const Threads: React.FC<ThreadsProps> = ({
amplitude = 1,
distance = 0,
enableMouseInteraction = false,
maxDevicePixelRatio = 1.5,
className,
...rest
}) => {
const containerRef = useRef<HTMLDivElement>(null);
const animationFrameId = useRef<number>(null);
const containerRef = useRef<HTMLDivElement | null>(null);
const rendererRef = useRef<Renderer | null>(null);
const programRef = useRef<Program | null>(null);
const meshRef = useRef<Mesh | null>(null);
const animationFrameRef = useRef<number | null>(null);
const visibleRef = useRef(true);
const mouseTargetRef = useRef<[number, number]>([0.5, 0.5]);
const mouseCurrentRef = useRef<[number, number]>([0.5, 0.5]);

useEffect(() => {
if (!containerRef.current) return;
const container = containerRef.current;

const renderer = new Renderer({ alpha: true });
rendererRef.current = renderer;
const gl = renderer.gl;

gl.clearColor(0, 0, 0, 0);
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);

container.appendChild(gl.canvas);

const geometry = new Triangle(gl);
const geometry = new Plane(gl, { width: 2, height: 2 });

const program = new Program(gl, {
vertex: vertexShader,
fragment: fragmentShader,
uniforms: {
iTime: { value: 0 },
iResolution: {
value: new Color(
gl.canvas.width,
gl.canvas.height,
gl.canvas.width / gl.canvas.height
),
},
uColor: { value: new Color(...color) },
iResolution: { value: new Float32Array([0, 0, 0]) },
uColor: { value: new Float32Array(color) },
uAmplitude: { value: amplitude },
uDistance: { value: distance },
uMouse: { value: new Float32Array([0.5, 0.5]) },
},
});

const mesh = new Mesh(gl, { geometry, program });
programRef.current = program;
meshRef.current = new Mesh(gl, { geometry, program });

const resize = () => {
const w = container.clientWidth;
const h = container.clientHeight;
renderer.setSize(w, h);

const res = program.uniforms.iResolution.value as Float32Array;
res[0] = renderer.width;
res[1] = renderer.height;
res[2] = w / h;
};

function resize() {
const { clientWidth, clientHeight } = container;
renderer.setSize(clientWidth, clientHeight);
program.uniforms.iResolution.value.r = clientWidth;
program.uniforms.iResolution.value.g = clientHeight;
program.uniforms.iResolution.value.b = clientWidth / clientHeight;
}
window.addEventListener("resize", resize);
resize();
window.addEventListener("resize", resize);

let currentMouse = [0.5, 0.5];
let targetMouse = [0.5, 0.5];
const observer = new IntersectionObserver((entries) => {
visibleRef.current = entries[0].isIntersecting;
});
observer.observe(container);

function handleMouseMove(e: MouseEvent) {
const rect = container.getBoundingClientRect();
const x = (e.clientX - rect.left) / rect.width;
const y = 1.0 - (e.clientY - rect.top) / rect.height;
targetMouse = [x, y];
}
function handleMouseLeave() {
targetMouse = [0.5, 0.5];
}
if (enableMouseInteraction) {
container.addEventListener("mousemove", handleMouseMove);
container.addEventListener("mouseleave", handleMouseLeave);
}
const animate = (t: number) => {
animationFrameRef.current = requestAnimationFrame(animate);

if (!visibleRef.current) return;

const m = program.uniforms.uMouse.value as Float32Array;
m.set(mouseCurrentRef.current);
Comment on lines +202 to +203
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Mouse uniform always stays at default value.

mouseCurrentRef.current is read here but is never updated anywhere in the component, so uMouse will always be [0.5, 0.5]. If mouse interaction is intended, you need to add mouse event handlers to update the ref.

🤖 Prompt for AI Agents
In LandingPage/src/components/bg.tsx around lines 202-203, the code reads
mouseCurrentRef.current but never updates it so the uMouse uniform stays at the
default; add mouse event handlers (mousemove and touchmove) that update
mouseCurrentRef.current with normalized coordinates (e.g., [x / canvasWidth, y /
canvasHeight] or flipped Y if shader expects it), attach those listeners on
mount and remove on unmount (or when the canvas element changes), ensure the ref
is initialized and throttled/debounced if needed to avoid performance issues,
and update any resize handler so normalization uses the current canvas size.


function update(t: number) {
if (enableMouseInteraction) {
const smoothing = 0.05;
currentMouse[0] += smoothing * (targetMouse[0] - currentMouse[0]);
currentMouse[1] += smoothing * (targetMouse[1] - currentMouse[1]);
program.uniforms.uMouse.value[0] = currentMouse[0];
program.uniforms.uMouse.value[1] = currentMouse[1];
} else {
program.uniforms.uMouse.value[0] = 0.5;
program.uniforms.uMouse.value[1] = 0.5;
}
program.uniforms.iTime.value = t * 0.001;

renderer.render({ scene: mesh });
animationFrameId.current = requestAnimationFrame(update);
}
animationFrameId.current = requestAnimationFrame(update);
renderer.render({ scene: meshRef.current! });
};

animationFrameRef.current = requestAnimationFrame(animate);

return () => {
if (animationFrameId.current)
cancelAnimationFrame(animationFrameId.current);
if (animationFrameRef.current) cancelAnimationFrame(animationFrameRef.current);
window.removeEventListener("resize", resize);

if (enableMouseInteraction) {
container.removeEventListener("mousemove", handleMouseMove);
container.removeEventListener("mouseleave", handleMouseLeave);
}
observer.disconnect();
if (container.contains(gl.canvas)) container.removeChild(gl.canvas);
gl.getExtension("WEBGL_lose_context")?.loseContext();
rendererRef.current = null;
programRef.current = null;
meshRef.current = null;
};
}, [color, amplitude, distance, enableMouseInteraction]);
}, []);

return (
<div ref={containerRef} className="w-full h-full relative" {...rest} />
<div
ref={containerRef}
className={className}
{...rest}
style={{
width: "100%",
height: "100%",
position: "relative",
overflow: "hidden",
...rest?.style,
}}
/>
);
};

Expand Down
Loading