/* global React, ReactDOM, Frame, useTweaks, TweaksPanel, TweakSection, TweakSlider, TweakToggle, TweakColor, TweakRadio */ const { useState, useEffect, useRef, useCallback, useMemo } = React; const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "accent": "#f4f1ea", "grain": 0.08, "motion": true, "scanlines": true, "heroLayout": "simple" }/*EDITMODE-END*/; /* ───────────── Work data ───────────── */ const WORKS = [ { num:'01', title:'Annibal', sub:'Spec Teaser · Wallyscar', yt:'gXfM4J00Qqs' }, { num:'02', title:'LostJeel', sub:'Spec Ad · LostJeel', yt:'4WWhF4q_ruo' }, { num:'03', title:'Boga Shhhh', sub:'Spec Ad · Boga', yt:'Gvk8XwSwhfw' }, { num:'04', title:'Sexy Fruit', sub:'Commercial · Dally', yt:'NefqDH1XSQU' }, { num:'05', title:'Short Movie POS', sub:'Dor Awards', yt:'evEIxpGc51Y' }, { num:'06', title:'Generic Juice', sub:'Spec Ad · Dally', yt:'EANB5iBsWl0' }, ]; const PROCESS = [ { n:'I', name:'Listen', it:'Listen' }, { n:'II', name:'Storyboard', it:'board' }, { n:'III', name:'Discuss', it:'Discuss' }, { n:'IV', name:'Refine', it:'Refine' }, { n:'V', name:'Generate', it:'Generate' }, { n:'VI', name:'Deliver', it:'Deliver' }, ]; const MARQUEE = ['Commercials','Virals — if they earn it','Short films','Music videos','Title sequences','Things friends ask me to make at midnight']; /* ───────────── Cursor ───────────── */ function Cursor() { const dot = useRef(null); const ring = useRef(null); useEffect(() => { let dx=0,dy=0,rx=0,ry=0,tx=0,ty=0; let raf; const onMove = (e) => { tx=e.clientX; ty=e.clientY; if(!raf) tick(); }; const tick = () => { dx += (tx-dx)*0.55; dy += (ty-dy)*0.55; rx += (tx-rx)*0.18; ry += (ty-ry)*0.18; if (dot.current) dot.current.style.transform = `translate(${dx}px, ${dy}px) translate(-50%,-50%)`; if (ring.current) ring.current.style.transform = `translate(${rx}px, ${ry}px) translate(-50%,-50%)`; raf = requestAnimationFrame(tick); }; const onOver = (e) => { const t = e.target.closest('a,button,input,textarea,select,[data-cursor]'); if (!ring.current) return; ring.current.classList.remove('is-link','is-play'); if (!t) return; const kind = t.getAttribute('data-cursor'); if (kind === 'play') ring.current.classList.add('is-play'); else ring.current.classList.add('is-link'); }; const onLeaveDoc = () => { dot.current?.classList.add('is-hide'); ring.current?.classList.add('is-hide'); }; const onEnterDoc = () => { dot.current?.classList.remove('is-hide'); ring.current?.classList.remove('is-hide'); }; window.addEventListener('mousemove', onMove); window.addEventListener('mouseover', onOver); document.addEventListener('mouseleave', onLeaveDoc); document.addEventListener('mouseenter', onEnterDoc); return () => { window.removeEventListener('mousemove', onMove); window.removeEventListener('mouseover', onOver); document.removeEventListener('mouseleave', onLeaveDoc); document.removeEventListener('mouseenter', onEnterDoc); cancelAnimationFrame(raf); }; }, []); return ( <>
); } /* ───────────── Magnetic wrapper ───────────── */ function Magnetic({ children, strength = 0.35, className }) { const ref = useRef(null); useEffect(() => { const el = ref.current; if (!el) return; const onMove = (e) => { const r = el.getBoundingClientRect(); const x = e.clientX - (r.left + r.width/2); const y = e.clientY - (r.top + r.height/2); el.style.transform = `translate(${x*strength}px, ${y*strength}px)`; }; const onLeave = () => { el.style.transform = ''; }; el.addEventListener('mousemove', onMove); el.addEventListener('mouseleave', onLeave); return () => { el.removeEventListener('mousemove', onMove); el.removeEventListener('mouseleave', onLeave); }; }, [strength]); return {children}; } /* ───────────── Reveal on scroll ───────────── */ function useReveal() { useEffect(() => { const els = document.querySelectorAll('.rv'); const io = new IntersectionObserver((entries) => { entries.forEach(e => { if (e.isIntersecting) { e.target.classList.add('in'); io.unobserve(e.target); } }); }, { threshold: 0.12 }); els.forEach(el => io.observe(el)); return () => io.disconnect(); }, []); } /* ───────────── Grain (generated once) ───────────── */ function makeGrainURL() { const c = document.createElement('canvas'); c.width = c.height = 220; const ctx = c.getContext('2d'); const img = ctx.createImageData(220, 220); for (let i=0; i { const start = performance.now(); let raf; const tick = () => { const ms = performance.now() - start; const total = Math.floor(ms / 1000); const hh = String(Math.floor(total/3600)).padStart(2,'0'); const mm = String(Math.floor(total/60)%60).padStart(2,'0'); const ss = String(total%60).padStart(2,'0'); const ff = String(Math.floor((ms%1000) / (1000/24))).padStart(2,'0'); setTc(`${hh}:${mm}:${ss}:${ff}`); raf = requestAnimationFrame(tick); }; raf = requestAnimationFrame(tick); return () => cancelAnimationFrame(raf); }, []); return {tc}; } function HeroAbout({ layout = 'simple' }) { if (layout === 'simple') { return (
Oussama Tarchouna — AI-native filmmaker

I make films with Generative AI the way other directors make them with crews — patiently, with a point of view, and never by accident.

I'm Oussama, an AI-native filmmaker working between Tunis and Rotterdam. I direct AI videos, commercials, viral pieces, and short films; using the latest generation of video models1.

Before all of this, I was a brand designer. Logos, brand identities, websites — I've worked across the full spectrum.

Moving into UX is what taught me to think like a viewer, not a maker. That perspective is what drives my approach to AI video: footage that doesn't feel AI, always in service of the brand (not the client).

— 1 Yes, the same models you've seen ruin LinkedIn. Used carefully, they are a camera. Used carelessly, they are an embarrassment. I treat that distinction as a job.
); } if (layout === 'split') { return (

I make films with Generative AI the way other directors make them with crews — patiently, with a point of view, and never by accident.

I'm Oussama, an AI-native filmmaker working between Tunis and Rotterdam. I direct AI videos, commercials, viral pieces, and short films; using the latest generation of video models1.

Before all of this, I was a brand designer. Logos, brand identities, websites — I've worked across the full spectrum.

Moving into UX is what taught me to think like a viewer, not a maker. That perspective is what drives my approach to AI video: footage that doesn't feel AI, always in service of the brand (not the client).

— 1 Yes, the same models you've seen ruin LinkedIn. Used carefully, they are a camera. Used carelessly, they are an embarrassment. I treat that distinction as a job.
); } // stacked (original) return (

O.Tarchouna

AI-native filmmaker · Commercials, virals, short films

I make films with Generative AI the way other directors make them with crews — patiently, with a point of view, and never by accident.

I'm Oussama, an AI-native filmmaker working between Tunis and Rotterdam. I direct AI videos, commercials, viral pieces, and short films; using the latest generation of video models1.

Before all of this, I was a brand designer. Logos, brand identities, websites — I've worked across the full spectrum.

Moving into UX is what taught me to think like a viewer, not a maker. That perspective is what drives my approach to AI video: footage that doesn't feel AI, always in service of the brand (not the client).

— 1 Yes, the same models you've seen ruin LinkedIn. Used carefully, they are a camera. Used carelessly, they are an embarrassment. I treat that distinction as a job.
Coordinates
Tunis · Rotterdam · remote everywhere else
(mostly Tunis, if I'm honest)
); } /* ───────────── Work rail ───────────── */ function WorkRail({ motion }) { const wrap = useRef(null); const [pct, setPct] = useState(0); useEffect(() => { const el = wrap.current; if (!el) return; const onScroll = () => { const max = el.scrollWidth - el.clientWidth; setPct(max > 0 ? (el.scrollLeft / max) * 100 : 0); }; el.addEventListener('scroll', onScroll, { passive:true }); onScroll(); return () => el.removeEventListener('scroll', onScroll); }, []); // wheel → horizontal useEffect(() => { const el = wrap.current; if (!el) return; const onWheel = (e) => { if (Math.abs(e.deltaY) > Math.abs(e.deltaX)) { const max = el.scrollWidth - el.clientWidth; if (max <= 0) return; const next = el.scrollLeft + e.deltaY; if (next >= 0 && next <= max) { el.scrollLeft = next; e.preventDefault(); } } }; el.addEventListener('wheel', onWheel, { passive:false }); return () => el.removeEventListener('wheel', onWheel); }, []); // keyboard useEffect(() => { const onKey = (e) => { if (!wrap.current) return; if (e.key === 'ArrowRight') wrap.current.scrollBy({ left: 600, behavior:'smooth' }); if (e.key === 'ArrowLeft') wrap.current.scrollBy({ left:-600, behavior:'smooth' }); }; window.addEventListener('keydown', onKey); return () => window.removeEventListener('keydown', onKey); }, []); return (
{WORKS.map((w, i) => ( e.preventDefault()} style={{ transitionDelay: `${i*0.05}s` }}>