✦ WOW PLAYBOOK SERIES

The WOW Prompt Bible — ספר הפרומפטים

כל אפקט, כל טכנולוגיה, כל פרומפט — במקום אחד

אפקטים
קטגוריות
טוען נתונים...
🔍
מציג 0/0
קטגוריה
רמה
מתחיל
בינוני
מתקדם
טכנולוגיה
CSS
JS
Canvas
WebGL
SVG
Three.js
🔭

לא נמצאו אפקטים

נסה חיפוש אחר או נקה את הפילטרים

\n\n// Hide loader when page is ready\nwindow.addEventListener('load', () => {\n setTimeout(() => {\n document.getElementById('loader').classList.add('hidden');\n }, 500); // minimum display time for polish\n});\n\n// For SPA: show/hide on route changes\nfunction showLoader() { document.getElementById('loader').classList.remove('hidden'); }\nfunction hideLoader() { document.getElementById('loader').classList.add('hidden'); }\n\nFree Lottie animations: Browse lottiefiles.com for thousands of free loader animations. Export as .json or .lottie format.", "code_hint": "\n\n\n\n", "technologies": [ "JavaScript" ], "difficulty": "beginner", "tags": [ "loading", "lottie", "animation", "branded", "JSON", "after-effects" ], "seen_in": "Airbnb, Google apps, TikTok web", "seen_in_url": "https://lottiefiles.com" }, { "id": "progress-bar-percentage", "name_en": "Progress Bar with Percentage", "name_he": "פס התקדמות עם אחוזים", "category": "loading-states", "description": "פס התקדמות אופקי דק בראש המסך (כמו YouTube או GitHub) שמתמלא מ-0% ל-100% תוך כדי טעינת הדף, ונעלם בסיום.", "prompt": "Build a top-of-page loading progress bar that fills from left to right with animated gradient and disappears when complete — like YouTube and GitHub page transitions.\n\nCSS:\n.progress-bar {\n position: fixed;\n top: 0;\n left: 0;\n width: 0%;\n height: 3px;\n background: linear-gradient(90deg, #7c3aed, #06b6d4, #7c3aed);\n background-size: 200% 100%;\n animation: gradient-shift 2s linear infinite;\n z-index: 99999;\n transition: width 0.3s ease;\n box-shadow: 0 0 8px rgba(124, 58, 237, 0.5);\n}\n\n.progress-bar.done {\n width: 100%;\n opacity: 0;\n transition: width 0.2s ease, opacity 0.4s ease 0.2s;\n}\n\n@keyframes gradient-shift {\n 0% { background-position: 0% 0; }\n 100% { background-position: 200% 0; }\n}\n\n/* Pulsing glow at the leading edge */\n.progress-bar::after {\n content: '';\n position: absolute;\n right: 0;\n top: -2px;\n width: 80px;\n height: 7px;\n background: radial-gradient(ellipse, rgba(6,182,212,0.6), transparent);\n border-radius: 50%;\n}\n\nJavaScript:\nclass ProgressBar {\n constructor() {\n this.el = document.createElement('div');\n this.el.className = 'progress-bar';\n document.body.prepend(this.el);\n this.progress = 0;\n }\n \n start() {\n this.el.classList.remove('done');\n this.progress = 0;\n this.trickle(); // auto-increment slowly\n }\n \n trickle() {\n this.timer = setInterval(() => {\n this.progress += Math.random() * 8 + 2;\n if (this.progress > 90) this.progress = 90; // never reach 100 until done\n this.el.style.width = this.progress + '%';\n }, 400);\n }\n \n done() {\n clearInterval(this.timer);\n this.el.style.width = '100%';\n this.el.classList.add('done');\n }\n}", "code_hint": "const bar = new ProgressBar();\n// On navigation start\nbar.start();\n// On page load complete\nbar.done();", "technologies": [ "CSS", "JavaScript" ], "difficulty": "beginner", "tags": [ "loading", "progress", "bar", "top-bar", "gradient", "page-load" ], "seen_in": "YouTube, GitHub, NProgress library", "seen_in_url": "https://ricostacruz.com/nprogress/" }, { "id": "dna-helix-spinner", "name_en": "DNA Helix Spinner", "name_he": "ספינר סליל DNA", "category": "loading-states", "description": "אנימציית טעינה מסתובבת של סליל כפול DNA — שני עקומות שזורות שמסתובבות סביב ציר מרכזי. משמש לביוטק, מדע, ומותגי היי-טק.", "prompt": "Create a CSS-only DNA double helix loading spinner with two intertwining curves rotating in 3D.\n\nHTML:\n
\n \n
\n \n \n \n
\n \n
\n\nCSS:\n.dna-helix {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 4px;\n height: 120px;\n}\n\n.helix-pair {\n display: flex;\n align-items: center;\n gap: 2px;\n animation: helix-rotate 1.5s ease-in-out infinite;\n animation-delay: calc(var(--i) * -0.15s);\n}\n\n@keyframes helix-rotate {\n 0% { transform: rotateY(0deg) scaleX(1); }\n 25% { transform: rotateY(90deg) scaleX(0.1); } /* edge-on = nearly invisible */\n 50% { transform: rotateY(180deg) scaleX(1); } /* flipped */\n 75% { transform: rotateY(270deg) scaleX(0.1); }\n 100% { transform: rotateY(360deg) scaleX(1); }\n}\n\n.dot {\n width: 10px;\n height: 10px;\n border-radius: 50%;\n}\n.dot-a { background: #7c3aed; }\n.dot-b { background: #06b6d4; }\n\n.bar {\n width: 30px;\n height: 2px;\n background: rgba(255,255,255,0.2);\n}\n\n/* The scaleX trick: at 90deg and 270deg, the pair is viewed edge-on so it appears as a thin line, creating the illusion of 3D rotation */\n\n/* The staggered animation-delay on each pair creates the wave/twist effect — each pair is slightly behind the one above, creating the helical rotation */", "code_hint": "@keyframes helix-rotate {\n 0% { transform: rotateY(0deg) scaleX(1); }\n 25% { transform: rotateY(90deg) scaleX(0.1); }\n 50% { transform: rotateY(180deg) scaleX(1); }\n 75% { transform: rotateY(270deg) scaleX(0.1); }\n 100% { transform: rotateY(360deg) scaleX(1); }\n}\n.helix-pair {\n animation: helix-rotate 1.5s ease-in-out infinite;\n animation-delay: calc(var(--i) * -0.15s);\n}", "technologies": [ "CSS" ], "difficulty": "intermediate", "tags": [ "loading", "spinner", "DNA", "helix", "3D", "CSS-only", "creative" ], "seen_in": "Biotech startup loaders, science-themed web apps", "seen_in_url": "https://freefrontend.com/css-skeleton-loadings/" }, { "id": "content-aware-skeleton", "name_en": "Content-Aware Skeleton", "name_he": "שלד טעינה מותאם תוכן", "category": "loading-states", "description": "צורות שלד שמשקפות בדיוק את הלייאאוט של התוכן האמיתי — כותרת, תת-כותרת, 3 שורות גוף, ואזור תמונה — כך שכשהתוכן נטען, הוא נכנס למקום בצורה מושלמת בלי קפיצת layout.", "prompt": "Build a content-aware skeleton loader that mirrors the exact layout of the final content, then dissolves to reveal the real content when loaded.\n\nHTML Structure:\n
\n \n
\n
\n
\n
\n
\n
\n
\n
\n \n
\n \n

Title

\n

Subtitle

\n

Body text...

\n
\n
\n\nCSS:\n.skel {\n background: #1a1a2e;\n border-radius: 6px;\n margin-bottom: 12px;\n animation: shimmer 1.5s ease-in-out infinite;\n background-image: linear-gradient(90deg, #1a1a2e, #2a2a4e 50%, #1a1a2e);\n background-size: 200% 100%;\n}\n\n/* Transition from skeleton to real content */\n[data-loading='false'] .skeleton-layer {\n opacity: 0;\n position: absolute; /* pull out of flow */\n transition: opacity 0.4s ease;\n}\n\n[data-loading='false'] .real-content {\n opacity: 1;\n transition: opacity 0.4s ease 0.1s; /* slight delay for smooth crossfade */\n}\n\nJavaScript:\n// When content is ready:\nfunction contentLoaded(card) {\n card.dataset.loading = 'false';\n}\n\nThe key insight: the skeleton shapes have the EXACT same dimensions (width percentages, heights, margins) as the real content — so when the swap happens, nothing jumps or shifts. This prevents CLS (Cumulative Layout Shift).", "code_hint": "/* Both layers occupy same space */\n.content-card { position: relative; }\n.skeleton-layer, .real-content {\n transition: opacity 0.4s ease;\n}\n[data-loading='true'] .real-content { opacity: 0; }\n[data-loading='false'] .skeleton-layer { opacity: 0; position: absolute; }\n[data-loading='false'] .real-content { opacity: 1; }", "technologies": [ "CSS", "JavaScript" ], "difficulty": "intermediate", "tags": [ "loading", "skeleton", "content-aware", "layout", "CLS", "UX" ], "seen_in": "Facebook feed, Instagram grid, modern SPAs", "seen_in_url": "https://www.matsimon.dev/blog/simple-skeleton-loaders" }, { "id": "konami-code", "name_en": "Konami Code Activation", "name_he": "קוד קונאמי", "category": "easter-eggs", "description": "הקלדת הרצף הסודי (למעלה, למעלה, למטה, למטה, שמאלה, ימינה, שמאלה, ימינה, B, A) מפעילה תוכן נסתר — אנימציה, מצב סודי, או הפתעה. השם מגיע ממשחקי Konami הקלאסיים.", "prompt": "Add a Konami Code easter egg to your website — when the user types the classic sequence (up up down down left right left right B A), trigger a hidden effect.\n\nJavaScript:\nconst konamiCode = ['ArrowUp','ArrowUp','ArrowDown','ArrowDown','ArrowLeft','ArrowRight','ArrowLeft','ArrowRight','b','a'];\nlet konamiIndex = 0;\n\ndocument.addEventListener('keydown', (e) => {\n const expected = konamiCode[konamiIndex];\n \n if (e.key === expected || e.key.toLowerCase() === expected) {\n konamiIndex++;\n \n if (konamiIndex === konamiCode.length) {\n konamiIndex = 0;\n activateEasterEgg();\n }\n } else {\n konamiIndex = 0; // reset on wrong key\n }\n});\n\nfunction activateEasterEgg() {\n // Option 1: Matrix rain effect\n document.body.classList.add('matrix-mode');\n \n // Option 2: Confetti explosion\n confettiExplosion();\n \n // Option 3: Secret page content swap\n document.querySelector('.hero-title').textContent = 'You found the secret!';\n \n // Option 4: Retro mode — pixelate everything\n document.body.style.filter = 'contrast(1.5) saturate(1.5)';\n document.body.style.imageRendering = 'pixelated';\n document.body.style.fontFamily = 'monospace';\n \n // Auto-revert after 10 seconds\n setTimeout(() => {\n document.body.classList.remove('matrix-mode');\n document.body.style.filter = '';\n }, 10000);\n}\n\nMobile support: For touch devices, detect a specific swipe pattern:\nup-swipe, up-swipe, down-swipe, down-swipe, left-swipe, right-swipe, left-swipe, right-swipe, double-tap\n\nStore activation in sessionStorage so it persists through page navigation.", "code_hint": "const konami = ['ArrowUp','ArrowUp','ArrowDown','ArrowDown','ArrowLeft','ArrowRight','ArrowLeft','ArrowRight','b','a'];\nlet idx = 0;\ndocument.addEventListener('keydown', (e) => {\n if (e.key === konami[idx] || e.key.toLowerCase() === konami[idx]) {\n if (++idx === konami.length) { idx = 0; activateEasterEgg(); }\n } else idx = 0;\n});", "technologies": [ "JavaScript" ], "difficulty": "beginner", "tags": [ "easter-egg", "konami", "hidden", "secret", "keyboard", "gamification" ], "seen_in": "ARES-X Mars terraforming easter egg, BuzzFeed, Digg", "seen_in_url": "https://www.viget.com/articles/breaking-the-konami-code-adding-an-easter-egg-to-your-site" }, { "id": "console-message-art", "name_en": "Console Message Art", "name_he": "הודעת ASCII בקונסול", "category": "easter-eggs", "description": "פתיחת כלי הפיתוח חושפת ASCII art מעוצב, הודעת גיוס, או ברכה סודית בקונסול הדפדפן. חברות כמו Facebook ו-Porsche משתמשות בזה כדי לדבר עם מפתחים.", "prompt": "Add ASCII art and a styled message to the browser developer console as a hidden easter egg for developers.\n\nJavaScript (place in main script or inline):\n// ASCII Art logo\nconsole.log(`\n%c\n ██████╗ ██████╗ █████╗ ███╗ ██╗██████╗ \n ██╔══██╗██╔══██╗██╔══██╗████╗ ██║██╔══██╗\n ██████╔╝██████╔╝███████║██╔██╗ ██║██║ ██║\n ██╔══██╗██╔══██╗██╔══██║██║╚██╗██║██║ ██║\n ██████╔╝██║ ██║██║ ██║██║ ╚████║██████╔╝\n ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═════╝ \n`,\n'color: #7c3aed; font-family: monospace; font-size: 10px;');\n\n// Styled welcome message\nconsole.log(\n '%cHey there, curious developer! 👋',\n 'color: #06b6d4; font-size: 18px; font-weight: bold; padding: 8px 0;'\n);\n\nconsole.log(\n '%cLike what you see under the hood? We\\'re hiring!\\nhttps://brand.com/careers',\n 'color: #e2e8f0; font-size: 14px; padding: 4px 0;'\n);\n\n// Warning for non-developers\nconsole.log(\n '%c⚠ STOP! This browser feature is for developers.',\n 'color: #f59e0b; font-size: 24px; font-weight: bold;'\n);\n\n// Fun: Add an interactive command\nwindow.secretMode = () => {\n document.body.classList.toggle('party-mode');\n console.log('%c🎉 Party mode activated!', 'font-size: 24px;');\n};\nconsole.log('%cType secretMode() for a surprise...', 'color: #34d399; font-style: italic;');", "code_hint": "console.log(\n '%cWelcome, curious developer!',\n 'color: #7c3aed; font-size: 20px; font-weight: bold;'\n);\nconsole.log(\n '%cWe\\'re hiring: https://brand.com/careers',\n 'color: #06b6d4; font-size: 14px;'\n);", "technologies": [ "JavaScript" ], "difficulty": "beginner", "tags": [ "easter-egg", "console", "ASCII", "developer", "hidden", "recruiting" ], "seen_in": "Facebook (warning message), Porsche, Netflix, Reddit", "seen_in_url": "https://facebook.com" }, { "id": "tab-away-title-change", "name_en": "Tab-Away Title Change", "name_he": "שינוי כותרת בעזיבת טאב", "category": "easter-eggs", "description": "כשהמשתמש עובר לטאב אחר, כותרת הדף משתנה ל\"Come back!\" או הודעה מצחיקה. כשחוזרים, הכותרת חוזרת למצב הרגיל. משתמש ב-Page Visibility API.", "prompt": "Implement a tab-away title change that updates the browser tab title when the user switches to another tab, and restores it when they return.\n\nJavaScript:\nconst originalTitle = document.title;\nconst awayTitle = 'Come back! We miss you...';\nconst awayFavicon = '😢'; // emoji favicon\nlet originalFavicon = null;\n\ndocument.addEventListener('visibilitychange', () => {\n if (document.hidden) {\n // User left the tab\n document.title = awayTitle;\n setEmojiFavicon(awayFavicon);\n } else {\n // User came back\n document.title = originalTitle;\n restoreOriginalFavicon();\n \n // Optional: show a welcome-back toast\n showToast('Welcome back! 👋');\n }\n});\n\n// Emoji favicon helper\nfunction setEmojiFavicon(emoji) {\n const link = document.querySelector('link[rel*=\"icon\"]') || document.createElement('link');\n originalFavicon = link.href;\n link.type = 'image/svg+xml';\n link.rel = 'icon';\n link.href = `data:image/svg+xml,${emoji}`;\n document.head.appendChild(link);\n}\n\nfunction restoreOriginalFavicon() {\n if (originalFavicon) {\n const link = document.querySelector('link[rel*=\"icon\"]');\n link.href = originalFavicon;\n }\n}\n\nVariations:\n- E-commerce: 'Forgot something in your cart?'\n- Game site: 'The game is still running!'\n- SaaS: 'Your data misses you'\n- Fun: Cycle through increasingly desperate messages every 5 seconds while away", "code_hint": "const originalTitle = document.title;\ndocument.addEventListener('visibilitychange', () => {\n document.title = document.hidden\n ? 'Come back! We miss you...'\n : originalTitle;\n});", "technologies": [ "JavaScript" ], "difficulty": "beginner", "tags": [ "easter-egg", "tab", "title", "visibility-api", "engagement", "hidden" ], "seen_in": "ARES-X 'Come Back, Commander!' effect, e-commerce sites", "seen_in_url": "https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API" }, { "id": "long-press-secret", "name_en": "Long-Press Secret", "name_he": "סוד לחיצה ארוכה", "category": "easter-eggs", "description": "לחיצה ארוכה (3 שניות) על אלמנט ספציפי — כמו הלוגו או תמונה — חושפת מצב סודי, תפריט נסתר, או מידע דיבוג. פס התקדמות מעגלי מראה כמה נשאר ללחוץ.", "prompt": "Create a long-press secret interaction where holding down on the logo for 3 seconds reveals hidden debug info or a secret mode, with a circular progress indicator showing hold progress.\n\nHTML:\n
\n \"Logo\"\n \n \n \n \n
\n\nCSS:\n.hold-progress {\n position: absolute;\n inset: -8px;\n width: calc(100% + 16px);\n height: calc(100% + 16px);\n transform: rotate(-90deg);\n opacity: 0;\n transition: opacity 0.2s;\n}\n\n.long-press-target.holding .hold-progress { opacity: 1; }\n\n.progress-ring {\n transition: stroke-dashoffset 3s linear; /* 3 second hold time */\n}\n\n.long-press-target.holding .progress-ring {\n stroke-dashoffset: 0; /* fills the ring */\n}\n\nJavaScript:\nconst target = document.getElementById('logo');\nlet holdTimer;\n\nfunction startHold() {\n target.classList.add('holding');\n holdTimer = setTimeout(() => {\n target.classList.remove('holding');\n activateSecret();\n }, 3000);\n}\n\nfunction cancelHold() {\n target.classList.remove('holding');\n clearTimeout(holdTimer);\n // Reset progress ring\n const ring = target.querySelector('.progress-ring');\n ring.style.transition = 'none';\n ring.style.strokeDashoffset = '283';\n requestAnimationFrame(() => ring.style.transition = '');\n}\n\ntarget.addEventListener('mousedown', startHold);\ntarget.addEventListener('mouseup', cancelHold);\ntarget.addEventListener('mouseleave', cancelHold);\ntarget.addEventListener('touchstart', startHold);\ntarget.addEventListener('touchend', cancelHold);", "code_hint": "let timer;\ntarget.addEventListener('mousedown', () => {\n target.classList.add('holding');\n timer = setTimeout(activateSecret, 3000);\n});\ntarget.addEventListener('mouseup', () => {\n target.classList.remove('holding');\n clearTimeout(timer);\n});", "technologies": [ "SVG", "CSS", "JavaScript" ], "difficulty": "intermediate", "tags": [ "easter-egg", "long-press", "hold", "secret", "progress-ring", "mobile" ], "seen_in": "iOS app debug modes, hidden settings in web apps", "seen_in_url": "https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events" }, { "id": "idle-screensaver", "name_en": "Idle Mode / Screensaver", "name_he": "מצב סרק / שומר מסך", "category": "easter-eggs", "description": "אם המשתמש לא מזיז את העכבר או גולל למשך 60 שניות, האתר עובר למצב \"שומר מסך\" — אנימציה צפה, ויזואליזציה, או מיני-משחק. כל תנועה מחזירה למצב רגיל.", "prompt": "Create an idle screensaver mode that activates after 60 seconds of user inactivity, showing a mesmerizing animation, and dismisses on any interaction.\n\nJavaScript:\nlet idleTimer;\nconst IDLE_TIMEOUT = 60000; // 60 seconds\n\nfunction resetIdleTimer() {\n clearTimeout(idleTimer);\n deactivateScreensaver();\n idleTimer = setTimeout(activateScreensaver, IDLE_TIMEOUT);\n}\n\nfunction activateScreensaver() {\n const overlay = document.getElementById('screensaver');\n overlay.classList.add('active');\n startScreensaverAnimation();\n}\n\nfunction deactivateScreensaver() {\n const overlay = document.getElementById('screensaver');\n if (overlay.classList.contains('active')) {\n overlay.classList.remove('active');\n stopScreensaverAnimation();\n }\n}\n\n// Listen for ANY user activity\n['mousemove', 'mousedown', 'keydown', 'scroll', 'touchstart'].forEach(event => {\n document.addEventListener(event, resetIdleTimer, { passive: true });\n});\n\nresetIdleTimer(); // Start tracking\n\nCSS:\n#screensaver {\n position: fixed;\n inset: 0;\n background: #000;\n z-index: 99999;\n opacity: 0;\n pointer-events: none;\n transition: opacity 1s ease;\n}\n\n#screensaver.active {\n opacity: 1;\n pointer-events: auto;\n cursor: none;\n}\n\nScreensaver Ideas:\n1. Bouncing logo (like DVD screensaver) — changes color when hitting edges\n2. Floating particles / constellation\n3. Matrix rain\n4. Slowly morphing gradient blobs\n5. Analog clock with sweeping second hand\n\nShow a subtle \"Move mouse to return\" text that fades in after 2s.", "code_hint": "let timer;\nfunction reset() {\n clearTimeout(timer);\n deactivate();\n timer = setTimeout(activate, 60000);\n}\n['mousemove','keydown','scroll','touchstart'].forEach(e =>\n document.addEventListener(e, reset, { passive: true })\n);\nreset();", "technologies": [ "JavaScript", "CSS" ], "difficulty": "intermediate", "tags": [ "easter-egg", "idle", "screensaver", "inactivity", "animation", "engagement" ], "seen_in": "Bloomberg Terminal web, creative portfolio sites", "seen_in_url": "https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout" }, { "id": "command-palette", "name_en": "Hidden Command Palette (Cmd+K)", "name_he": "פלטת פקודות נסתרת", "category": "easter-eggs", "description": "Cmd+K (או Ctrl+K) פותח חיפוש מהיר כמו Spotlight של Mac — עם fuzzy search, ניווט מקלדת, וקבוצות פקודות (ניווט, פעולות, הגדרות). כמו CommandBar של Plan-B או kbar.", "prompt": "Build a Cmd+K command palette / spotlight search overlay with fuzzy search, keyboard navigation, and grouped actions.\n\nHTML:\n
\n
\n
\n \n \n ESC\n
\n
\n \n
\n
\n ↑↓ Navigate\n Select\n ESC Close\n
\n
\n
\n\nCSS:\n.cmd-palette-backdrop {\n position: fixed; inset: 0;\n background: rgba(0,0,0,0.5);\n backdrop-filter: blur(4px);\n z-index: 9999;\n display: flex; align-items: flex-start; justify-content: center;\n padding-top: 20vh;\n opacity: 0; pointer-events: none;\n transition: opacity 0.15s;\n}\n.cmd-palette-backdrop.open { opacity: 1; pointer-events: auto; }\n\n.cmd-palette {\n width: 560px; max-height: 400px;\n background: #1a1a2e;\n border: 1px solid rgba(255,255,255,0.1);\n border-radius: 16px;\n overflow: hidden;\n box-shadow: 0 24px 48px rgba(0,0,0,0.4);\n transform: scale(0.96);\n transition: transform 0.15s;\n}\n.cmd-palette-backdrop.open .cmd-palette { transform: scale(1); }\n\nJavaScript:\n// Open with Cmd+K / Ctrl+K\ndocument.addEventListener('keydown', (e) => {\n if ((e.metaKey || e.ctrlKey) && e.key === 'k') {\n e.preventDefault();\n togglePalette();\n }\n if (e.key === 'Escape') closePalette();\n});\n\n// Define commands\nconst commands = [\n { group: 'Navigation', label: 'Home', action: () => navigate('/'), icon: '🏠' },\n { group: 'Navigation', label: 'About', action: () => navigate('/about'), icon: '👤' },\n { group: 'Actions', label: 'Toggle Dark Mode', action: toggleDark, icon: '🌙' },\n { group: 'Actions', label: 'Contact Us', action: () => window.open('mailto:...'), icon: '✉' },\n];\n\n// Fuzzy search filter on input\n// Arrow key navigation with active index\n// Enter to execute selected command", "code_hint": "document.addEventListener('keydown', (e) => {\n if ((e.metaKey || e.ctrlKey) && e.key === 'k') {\n e.preventDefault();\n backdrop.classList.toggle('open');\n input.focus();\n }\n if (e.key === 'Escape') backdrop.classList.remove('open');\n});\n\ninput.addEventListener('input', () => {\n const q = input.value.toLowerCase();\n const filtered = commands.filter(c => c.label.toLowerCase().includes(q));\n renderResults(filtered);\n});", "technologies": [ "CSS", "JavaScript" ], "difficulty": "advanced", "tags": [ "easter-egg", "command-palette", "spotlight", "search", "keyboard", "Cmd+K" ], "seen_in": "Plan-B CommandBar, Linear, Notion, Slack, VS Code", "seen_in_url": "https://github.com/timc1/kbar" }, { "id": "achievement-system", "name_en": "Achievement / Gamification System", "name_he": "מערכת הישגים", "category": "easter-eggs", "description": "מערכת הישגים שמתגמלת משתמשים על גילוי תוכן, ביצוע פעולות, או אינטראקציות ספציפיות — כמו badges שנפתחים עם אנימציית pop-up. ARES-X משתמש במערכת כזו עם 15+ הישגים.", "prompt": "Build a gamification achievement system that awards badges for specific user interactions, with pop-up notifications and a trophy case view.\n\nJavaScript — Achievement Engine:\nconst achievements = {\n 'first-visit': { title: 'Explorer', desc: 'Visited the site for the first time', icon: '🧭' },\n 'scroll-bottom': { title: 'Deep Diver', desc: 'Scrolled to the bottom of the page', icon: '🤿' },\n 'dark-mode': { title: 'Night Owl', desc: 'Activated dark mode', icon: '🦉' },\n 'easter-egg': { title: 'Hacker', desc: 'Found the Konami code', icon: '🔓' },\n 'five-pages': { title: 'Navigator', desc: 'Visited 5 different pages', icon: '🗺' },\n 'speed-reader': { title: 'Speed Reader', desc: 'Read an article in under 2 minutes', icon: '⚡' },\n};\n\nfunction unlock(id) {\n if (localStorage.getItem(`ach-${id}`)) return; // already unlocked\n localStorage.setItem(`ach-${id}`, Date.now());\n showAchievementPopup(achievements[id]);\n}\n\nfunction showAchievementPopup(ach) {\n const popup = document.createElement('div');\n popup.className = 'achievement-popup';\n popup.innerHTML = `\n ${ach.icon}\n
\n Achievement Unlocked!\n

${ach.title} — ${ach.desc}

\n
\n `;\n document.body.appendChild(popup);\n \n // Animate in\n requestAnimationFrame(() => popup.classList.add('show'));\n setTimeout(() => {\n popup.classList.remove('show');\n setTimeout(() => popup.remove(), 500);\n }, 4000);\n}\n\nCSS:\n.achievement-popup {\n position: fixed;\n bottom: 24px; right: 24px;\n display: flex; align-items: center; gap: 12px;\n background: #1a1a2e;\n border: 1px solid #7c3aed;\n border-radius: 12px;\n padding: 16px 20px;\n transform: translateY(120%);\n transition: transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);\n z-index: 9999;\n box-shadow: 0 0 20px rgba(124,58,237,0.3);\n}\n.achievement-popup.show { transform: translateY(0); }\n.ach-icon { font-size: 36px; }\n\nTrigger Examples:\n- unlock('first-visit') on page load if not in localStorage\n- unlock('scroll-bottom') via IntersectionObserver on footer\n- unlock('dark-mode') when theme toggle is clicked", "code_hint": "function unlock(id) {\n if (localStorage.getItem(`ach-${id}`)) return;\n localStorage.setItem(`ach-${id}`, Date.now());\n showPopup(achievements[id]);\n}\n// Trigger: scroll to bottom\nconst observer = new IntersectionObserver((entries) => {\n if (entries[0].isIntersecting) unlock('scroll-bottom');\n});\nobserver.observe(document.querySelector('footer'));", "technologies": [ "JavaScript", "CSS" ], "difficulty": "intermediate", "tags": [ "easter-egg", "achievement", "gamification", "badge", "localStorage", "engagement" ], "seen_in": "ARES-X achievement system (15+ badges), Duolingo, Khan Academy", "seen_in_url": "https://ares-x.com" }, { "id": "dynamic-favicon", "name_en": "Dynamic Favicon per Route", "name_he": "פאביקון דינמי לפי דף", "category": "easter-eggs", "description": "הפאביקון בטאב הדפדפן משתנה בהתאם לדף שבו המשתמש נמצא — emoji שונה או אייקון שונה לכל מסך. ב-ARES-X הפאביקון משתנה לאימוג'י רלוונטי בכל route.", "prompt": "Implement a dynamic favicon that changes based on the current page route or application state.\n\nJavaScript:\nfunction setFavicon(emoji) {\n const svg = `\n ${emoji}\n `;\n \n let link = document.querySelector('link[rel~=\"icon\"]');\n if (!link) {\n link = document.createElement('link');\n link.rel = 'icon';\n document.head.appendChild(link);\n }\n link.type = 'image/svg+xml';\n link.href = `data:image/svg+xml,${encodeURIComponent(svg)}`;\n}\n\n// Route-based favicons\nconst routeFavicons = {\n '/': '🏠',\n '/about': '👤',\n '/products': '🛍',\n '/blog': '📝',\n '/contact': '✉',\n '/pricing': '💰',\n '/docs': '📖',\n};\n\n// Set on page load\nfunction updateFavicon() {\n const path = window.location.pathname;\n const emoji = routeFavicons[path] || '🌐';\n setFavicon(emoji);\n}\n\nupdateFavicon();\n\n// For SPAs: update on route change\n// In React: useEffect(() => updateFavicon(), [location.pathname]);\n\nAdvanced: Notification badge on favicon:\nfunction setFaviconWithBadge(emoji, count) {\n const svg = `\n ${emoji}\n ${count > 0 ? `\n ${count}` : ''}\n `;\n // ... set as favicon\n}", "code_hint": "function setFavicon(emoji) {\n let link = document.querySelector('link[rel~=\"icon\"]') || document.createElement('link');\n link.rel = 'icon';\n link.type = 'image/svg+xml';\n link.href = `data:image/svg+xml,${encodeURIComponent(\n `${emoji}`\n )}`;\n document.head.appendChild(link);\n}", "technologies": [ "JavaScript" ], "difficulty": "beginner", "tags": [ "easter-egg", "favicon", "dynamic", "route", "emoji", "SVG" ], "seen_in": "ARES-X route-based favicons, Slack notification badges", "seen_in_url": "https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link" }, { "id": "terraforming-easter-egg", "name_en": "Terraforming Easter Egg (Color Shift)", "name_he": "ביצת פסחא טרפורמינג", "category": "easter-eggs", "description": "דאבל-קליק על אלמנט מפעיל שינוי צבעים דרמטי — כמו ב-ARES-X שדאבל-קליק על מאדים הופך אותו ירוק/כחול (\"Terraformed Mars, Year 2340\"). CSS hue-rotate על כל האלמנט.", "prompt": "Create a 'terraforming' easter egg where double-clicking a specific element triggers a dramatic color transformation of the entire page or section.\n\nJavaScript:\nconst target = document.querySelector('.hero-image'); // or any element\nlet terraformed = false;\n\ntarget.addEventListener('dblclick', () => {\n terraformed = !terraformed;\n \n if (terraformed) {\n // Terraform! Shift all colors\n document.body.classList.add('terraformed');\n \n // Show announcement\n showToast('🌍 Terraformed! Year 2340 — Earth 2.0');\n \n // Optional: store in localStorage for persistence\n localStorage.setItem('terraformed', 'true');\n } else {\n document.body.classList.remove('terraformed');\n showToast('🔴 Reverted to original state');\n localStorage.removeItem('terraformed');\n }\n});\n\n// Restore on page load\nif (localStorage.getItem('terraformed')) {\n document.body.classList.add('terraformed');\n}\n\nCSS:\n.terraformed {\n transition: filter 2s ease-in-out;\n filter: hue-rotate(95deg) saturate(1.4) brightness(1.1);\n}\n\n/* More targeted: only shift certain elements */\n.terraformed .hero-image {\n filter: hue-rotate(95deg) saturate(1.6) brightness(1.15);\n}\n\n.terraformed .section-bg {\n filter: hue-rotate(120deg);\n}\n\n/* Add a subtle \"scanning\" overlay animation during transition */\n.terraformed::after {\n content: '';\n position: fixed;\n inset: 0;\n background: linear-gradient(transparent 50%, rgba(0, 255, 100, 0.02) 50%);\n background-size: 100% 4px;\n pointer-events: none;\n z-index: 9999;\n animation: scan-down 3s linear forwards;\n}\n\n@keyframes scan-down {\n from { transform: translateY(-100%); }\n to { transform: translateY(100%); opacity: 0; }\n}", "code_hint": "target.addEventListener('dblclick', () => {\n terraformed = !terraformed;\n document.body.classList.toggle('terraformed', terraformed);\n});\n\n/* CSS */\n.terraformed {\n filter: hue-rotate(95deg) saturate(1.4) brightness(1.1);\n transition: filter 2s ease-in-out;\n}", "technologies": [ "CSS", "JavaScript" ], "difficulty": "beginner", "tags": [ "easter-egg", "color-shift", "hue-rotate", "double-click", "terraforming", "filter" ], "seen_in": "ARES-X Mars globe double-click terraforming", "seen_in_url": "https://developer.mozilla.org/en-US/docs/Web/CSS/filter" }, { "id": "scratch-card-reveal", "name_en": "Scratch-Card Content Reveal", "name_he": "חשיפת תוכן בגירוד", "category": "easter-eggs", "description": "תוכן מוסתר מתחת לשכבה אפורה שהמשתמש \"מגרד\" עם העכבר לחשוף — כמו כרטיס גירוד של לוטו. Canvas מצייר עיגולים שקופים בנתיב העכבר.", "prompt": "Build a scratch-card reveal effect where users drag their cursor to 'scratch off' a covering layer and reveal hidden content beneath.\n\nHTML:\n
\n
\n

🎉 You won 20% off!

\n

Use code: SECRET20

\n
\n \n
\n\nCSS:\n.scratch-card {\n position: relative;\n width: 400px;\n height: 250px;\n border-radius: 16px;\n overflow: hidden;\n}\n\n.scratch-content {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n background: linear-gradient(135deg, #7c3aed, #06b6d4);\n color: white;\n}\n\n.scratch-canvas {\n position: absolute;\n inset: 0;\n cursor: crosshair;\n}\n\nJavaScript:\nconst canvas = document.getElementById('scratchCanvas');\nconst ctx = canvas.getContext('2d');\nlet isScratching = false;\n\n// Size canvas\ncanvas.width = canvas.offsetWidth * window.devicePixelRatio;\ncanvas.height = canvas.offsetHeight * window.devicePixelRatio;\nctx.scale(window.devicePixelRatio, window.devicePixelRatio);\n\n// Draw the covering layer\nctx.fillStyle = '#2a2a4e';\nctx.fillRect(0, 0, canvas.offsetWidth, canvas.offsetHeight);\n\n// Add \"Scratch here!\" text\nctx.fillStyle = '#8892a4';\nctx.font = '20px sans-serif';\nctx.textAlign = 'center';\nctx.fillText('Scratch to reveal!', canvas.offsetWidth / 2, canvas.offsetHeight / 2);\n\n// Scratch on drag\ncanvas.addEventListener('mousedown', () => isScratching = true);\ncanvas.addEventListener('mouseup', () => isScratching = false);\ncanvas.addEventListener('mousemove', scratch);\ncanvas.addEventListener('touchmove', (e) => {\n e.preventDefault();\n scratch(e.touches[0]);\n});\n\nfunction scratch(e) {\n if (!isScratching && e.type === 'mousemove') return;\n const rect = canvas.getBoundingClientRect();\n const x = e.clientX - rect.left;\n const y = e.clientY - rect.top;\n \n ctx.globalCompositeOperation = 'destination-out'; // erase mode\n ctx.beginPath();\n ctx.arc(x, y, 25, 0, Math.PI * 2);\n ctx.fill();\n \n checkRevealPercentage();\n}\n\n// Auto-reveal when 50% scratched\nfunction checkRevealPercentage() {\n const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);\n let transparent = 0;\n for (let i = 3; i < imageData.data.length; i += 4) {\n if (imageData.data[i] === 0) transparent++;\n }\n if (transparent / (imageData.data.length / 4) > 0.5) {\n canvas.style.transition = 'opacity 0.5s';\n canvas.style.opacity = '0';\n }\n}", "code_hint": "ctx.globalCompositeOperation = 'destination-out';\ncanvas.addEventListener('mousemove', (e) => {\n if (!scratching) return;\n const rect = canvas.getBoundingClientRect();\n ctx.beginPath();\n ctx.arc(e.clientX - rect.left, e.clientY - rect.top, 25, 0, Math.PI * 2);\n ctx.fill();\n});", "technologies": [ "Canvas", "JavaScript", "CSS" ], "difficulty": "intermediate", "tags": [ "easter-egg", "scratch-card", "reveal", "canvas", "interactive", "gamification" ], "seen_in": "Promotional landing pages, contest reveals, coupon sites", "seen_in_url": "https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation" }, { "id": "before-after-slider", "name_en": "Before/After Image Slider", "name_he": "סליידר לפני/אחרי", "category": "image-effects", "description": "שתי תמונות מונחות זו על זו, עם ידית גרירה אנכית במרכז. גרירה שמאלה חושפת יותר מתמונת ה\"לפני\", ימינה יותר מה\"אחרי\". משמש לתצוגת טרנספורמציות.", "prompt": "Build a before/after image comparison slider with a draggable vertical divider handle.\n\nHTML:\n
\n
\n \"Before\"\n
\n
\n \"After\"\n
\n
\n
\n
\n \n
\n
\n
\n
\n\nCSS:\n.comparison-slider {\n position: relative;\n width: 100%;\n max-width: 800px;\n aspect-ratio: 16/9;\n overflow: hidden;\n border-radius: 16px;\n cursor: ew-resize;\n user-select: none;\n}\n\n.comparison-before, .comparison-after {\n position: absolute;\n inset: 0;\n}\n\n.comparison-before {\n clip-path: inset(0 50% 0 0); /* clips right side */\n}\n\n.comparison-before img, .comparison-after img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n\n.comparison-handle {\n position: absolute;\n top: 0;\n left: 50%;\n width: 4px;\n height: 100%;\n display: flex;\n flex-direction: column;\n align-items: center;\n z-index: 10;\n transform: translateX(-50%);\n}\n\n.handle-line {\n flex: 1;\n width: 2px;\n background: white;\n box-shadow: 0 0 4px rgba(0,0,0,0.3);\n}\n\n.handle-circle {\n width: 44px;\n height: 44px;\n border-radius: 50%;\n background: white;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n box-shadow: 0 2px 8px rgba(0,0,0,0.3);\n font-size: 14px;\n}\n\nJavaScript:\nconst slider = document.getElementById('comparison');\nlet isDragging = false;\n\nfunction updatePosition(x) {\n const rect = slider.getBoundingClientRect();\n let percent = ((x - rect.left) / rect.width) * 100;\n percent = Math.max(0, Math.min(100, percent));\n \n slider.querySelector('.comparison-before').style.clipPath = `inset(0 ${100 - percent}% 0 0)`;\n slider.querySelector('.comparison-handle').style.left = percent + '%';\n}\n\nslider.addEventListener('mousedown', () => isDragging = true);\ndocument.addEventListener('mouseup', () => isDragging = false);\ndocument.addEventListener('mousemove', (e) => {\n if (isDragging) updatePosition(e.clientX);\n});\n\n// Touch support\nslider.addEventListener('touchmove', (e) => {\n updatePosition(e.touches[0].clientX);\n});", "code_hint": "function updatePosition(x) {\n const rect = slider.getBoundingClientRect();\n let pct = Math.max(0, Math.min(100, ((x - rect.left) / rect.width) * 100));\n before.style.clipPath = `inset(0 ${100-pct}% 0 0)`;\n handle.style.left = pct + '%';\n}\nslider.addEventListener('mousemove', e => { if (dragging) updatePosition(e.clientX); });\nslider.addEventListener('touchmove', e => updatePosition(e.touches[0].clientX));", "technologies": [ "CSS", "JavaScript" ], "difficulty": "intermediate", "tags": [ "image", "before-after", "slider", "comparison", "drag", "clip-path" ], "seen_in": "Real estate renovation sites, photo editing apps, dental/cosmetic sites", "seen_in_url": "https://github.com/sneas/img-comparison-slider" }, { "id": "ken-burns-effect", "name_en": "Ken Burns Pan & Zoom", "name_he": "אפקט קן ברנס", "category": "image-effects", "description": "תמונה סטטית שנעה ומתקרבת לאט — כמו מצלמה שסורקת תמונה בסרט תיעודי. טכניקת הצילום הקלאסית של קן ברנס שמחייה תמונות סטיליות. CSS בלבד.", "prompt": "Apply a Ken Burns slow pan-and-zoom effect to a hero image using pure CSS — no JavaScript needed.\n\nCSS:\n.ken-burns-container {\n width: 100%;\n height: 80vh;\n overflow: hidden; /* critical — hides the zoomed-out edges */\n position: relative;\n}\n\n.ken-burns-container img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n animation: ken-burns 20s ease-in-out infinite alternate;\n /* 'alternate' reverses direction each cycle for continuous back-and-forth */\n}\n\n@keyframes ken-burns {\n 0% {\n transform: scale(1) translate(0, 0);\n }\n 100% {\n transform: scale(1.15) translate(-3%, -2%);\n /* Zoom in 15% while panning slightly left and up */\n }\n}\n\nVariations:\n/* Pan right while zooming */\n@keyframes ken-burns-right {\n 0% { transform: scale(1.1) translate(-5%, 0); }\n 100% { transform: scale(1.2) translate(5%, -3%); }\n}\n\n/* Slow zoom out (reverse Ken Burns) */\n@keyframes ken-burns-out {\n 0% { transform: scale(1.2) translate(-2%, -2%); }\n 100% { transform: scale(1); }\n}\n\nFor a slideshow: apply different Ken Burns variations to each slide and swap with opacity crossfade:\n.slide:nth-child(1) img { animation: ken-burns-right 12s linear infinite alternate; }\n.slide:nth-child(2) img { animation: ken-burns 15s linear infinite alternate; }\n\nAccessibility:\n@media (prefers-reduced-motion: reduce) {\n .ken-burns-container img { animation: none; transform: scale(1.05); }\n}", "code_hint": ".ken-burns-container { overflow: hidden; }\n.ken-burns-container img {\n animation: ken-burns 20s ease-in-out infinite alternate;\n}\n@keyframes ken-burns {\n from { transform: scale(1) translate(0, 0); }\n to { transform: scale(1.15) translate(-3%, -2%); }\n}", "technologies": [ "CSS" ], "difficulty": "beginner", "tags": [ "image", "ken-burns", "pan", "zoom", "animation", "CSS-only", "documentary" ], "seen_in": "Apple TV+ show intros, Squarespace galleries, real estate hero sections", "seen_in_url": "https://en.wikipedia.org/wiki/Ken_Burns_effect" }, { "id": "pixelation-reveal", "name_en": "Pixelation Reveal", "name_he": "חשיפה בפיקסלציה", "category": "image-effects", "description": "תמונה שמתחילה כפיקסלים גדולים מטושטשים שמתחדדים בהדרגה עד לתמונה המלאה החדה — כמו צפייה ב-JPEG שנטען לאט. אפקט nostalgic שמשתמש ב-Canvas.", "prompt": "Build a canvas-based pixelation reveal effect where an image starts as large pixel blocks and progressively sharpens to full resolution when scrolled into view.\n\nHTML:\n\n\nJavaScript:\nconst canvas = document.getElementById('pixelCanvas');\nconst ctx = canvas.getContext('2d');\nconst img = new Image();\n\nimg.onload = () => {\n canvas.width = img.width;\n canvas.height = img.height;\n startPixelReveal();\n};\nimg.src = canvas.dataset.src;\n\nfunction drawPixelated(blockSize) {\n // Draw image at tiny size, then scale up (nearest-neighbor = pixelated)\n const w = canvas.width;\n const h = canvas.height;\n const smallW = Math.ceil(w / blockSize);\n const smallH = Math.ceil(h / blockSize);\n \n ctx.imageSmoothingEnabled = false; // nearest-neighbor scaling\n \n // Draw at small size\n ctx.drawImage(img, 0, 0, smallW, smallH);\n // Scale up to full size (pixelated)\n ctx.drawImage(canvas, 0, 0, smallW, smallH, 0, 0, w, h);\n}\n\nfunction startPixelReveal() {\n const steps = [32, 16, 8, 4, 2, 1]; // block sizes: large to full res\n let currentStep = 0;\n \n const observer = new IntersectionObserver((entries) => {\n if (entries[0].isIntersecting) {\n const interval = setInterval(() => {\n drawPixelated(steps[currentStep]);\n currentStep++;\n if (currentStep >= steps.length) {\n clearInterval(interval);\n // Final: draw crisp full image\n ctx.imageSmoothingEnabled = true;\n ctx.drawImage(img, 0, 0);\n }\n }, 150); // 150ms between each resolution step\n observer.disconnect();\n }\n }, { threshold: 0.3 });\n \n observer.observe(canvas);\n}\n\nCSS:\n.pixel-reveal {\n width: 100%;\n height: auto;\n border-radius: 12px;\n image-rendering: pixelated; /* keep crisp pixels during scaling */\n}", "code_hint": "function drawPixelated(blockSize) {\n const w = canvas.width, h = canvas.height;\n ctx.imageSmoothingEnabled = false;\n ctx.drawImage(img, 0, 0, Math.ceil(w/blockSize), Math.ceil(h/blockSize));\n ctx.drawImage(canvas, 0, 0, Math.ceil(w/blockSize), Math.ceil(h/blockSize), 0, 0, w, h);\n}", "technologies": [ "Canvas", "JavaScript" ], "difficulty": "intermediate", "tags": [ "image", "pixelation", "reveal", "canvas", "retro", "progressive" ], "seen_in": "Portfolio reveal animations, gaming websites, retro-themed landing pages", "seen_in_url": "https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/imageSmoothingEnabled" }, { "id": "lenticular-flip", "name_en": "Lenticular Flip Image", "name_he": "תמונת לנטיקולר", "category": "image-effects", "description": "תנועת העכבר אופקית מעל תמונה גורמת למעבר חלק בין שתי תמונות שונות לגמרי — כמו הדפס לנטיקולרי על כרטיסים פיזיים. המעבר עוקב אחרי מיקום הסמן.", "prompt": "Create a lenticular image effect where moving the cursor horizontally across an image smoothly transitions between two different images.\n\nHTML:\n
\n \"View\n \"View\n
\n\nCSS:\n.lenticular {\n position: relative;\n width: 100%;\n max-width: 600px;\n aspect-ratio: 3/4;\n overflow: hidden;\n border-radius: 16px;\n cursor: ew-resize;\n}\n\n.lenticular img {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n\n.lenticular-a {\n clip-path: polygon(0 0, var(--split, 50%) 0, var(--split, 50%) 100%, 0 100%);\n}\n\n.lenticular-b {\n clip-path: polygon(var(--split, 50%) 0, 100% 0, 100% 100%, var(--split, 50%) 100%);\n}\n\nJavaScript (smooth method):\nconst el = document.getElementById('lenticular');\n\nel.addEventListener('mousemove', (e) => {\n const rect = el.getBoundingClientRect();\n const x = (e.clientX - rect.left) / rect.width;\n el.style.setProperty('--split', `${x * 100}%`);\n});\n\nel.addEventListener('mouseleave', () => {\n el.style.setProperty('--split', '50%');\n});\n\nAdvanced (crossfade blend instead of hard split):\n.lenticular-b {\n opacity: var(--blend, 0.5);\n mix-blend-mode: normal;\n}\n\nel.addEventListener('mousemove', (e) => {\n const x = (e.clientX - el.getBoundingClientRect().left) / el.offsetWidth;\n el.style.setProperty('--blend', x); // 0 = all image A, 1 = all image B\n});\n\nTouch support: use touchmove with e.touches[0].clientX.", "code_hint": "el.addEventListener('mousemove', (e) => {\n const rect = el.getBoundingClientRect();\n const x = ((e.clientX - rect.left) / rect.width) * 100;\n el.style.setProperty('--split', x + '%');\n});\nel.addEventListener('mouseleave', () => {\n el.style.setProperty('--split', '50%');\n});", "technologies": [ "CSS", "JavaScript" ], "difficulty": "intermediate", "tags": [ "image", "lenticular", "flip", "cursor", "clip-path", "comparison" ], "seen_in": "Product photography, real estate virtual tours, art galleries", "seen_in_url": "https://freefrontend.com/javascript-holographic-effect/" }, { "id": "image-glitch-rgb-split", "name_en": "Image Glitch RGB Split", "name_he": "גליץ' תמונה עם פיצול RGB", "category": "image-effects", "description": "תמונה שמתפצלת לשכבות אדום, ירוק, כחול בריחוף — כל ערוץ צבע נע קלות בכיוון אחר, יוצר אפקט גליץ' דיגיטלי / עיוות VHS.", "prompt": "Create a CSS image glitch effect with RGB channel splitting on hover — the image splits into its red, green, and blue channels, each slightly offset.\n\nHTML:\n
\n \"\"\n
\n\nCSS:\n.glitch-image {\n position: relative;\n overflow: hidden;\n border-radius: 12px;\n}\n\n.glitch-image img {\n display: block;\n width: 100%;\n}\n\n/* RGB split layers using pseudo-elements */\n.glitch-image::before,\n.glitch-image::after {\n content: '';\n position: absolute;\n inset: 0;\n background: inherit;\n background-size: cover;\n opacity: 0;\n transition: opacity 0.2s;\n pointer-events: none;\n}\n\n/* Red channel — shifted left */\n.glitch-image::before {\n background: url('photo.jpg') center/cover;\n mix-blend-mode: screen;\n filter: grayscale(1) brightness(0) invert(22%) sepia(100%) saturate(7000%) hue-rotate(0deg); /* isolate red */\n}\n\n/* Cyan channel — shifted right */\n.glitch-image::after {\n background: url('photo.jpg') center/cover;\n mix-blend-mode: screen;\n filter: grayscale(1) brightness(0) invert(65%) sepia(100%) saturate(3000%) hue-rotate(150deg); /* isolate cyan */\n}\n\n.glitch-image:hover::before {\n opacity: 0.8;\n transform: translate(-4px, 2px);\n animation: glitch-r 0.3s steps(2) infinite;\n}\n\n.glitch-image:hover::after {\n opacity: 0.8;\n transform: translate(4px, -2px);\n animation: glitch-b 0.3s steps(3) infinite;\n}\n\n@keyframes glitch-r {\n 0% { transform: translate(-4px, 2px); }\n 33% { transform: translate(2px, -1px); }\n 66% { transform: translate(-2px, 3px); }\n 100% { transform: translate(-4px, 2px); }\n}\n\n@keyframes glitch-b {\n 0% { transform: translate(4px, -2px); }\n 33% { transform: translate(-3px, 1px); }\n 66% { transform: translate(3px, -3px); }\n 100% { transform: translate(4px, -2px); }\n}\n\nSimpler alternative (CSS filter only):\n.glitch-image:hover img {\n animation: simple-glitch 0.15s steps(1) infinite;\n}\n@keyframes simple-glitch {\n 0% { filter: none; transform: translate(0); }\n 20% { filter: hue-rotate(90deg); transform: translate(-3px, 2px); }\n 40% { filter: saturate(3) contrast(1.5); transform: translate(3px, -2px); }\n 60% { filter: invert(0.1); transform: translate(-2px, -1px); }\n 80% { filter: hue-rotate(-90deg); transform: translate(2px, 1px); }\n}", "code_hint": "/* Simple CSS glitch on hover */\n.glitch-image:hover img {\n animation: glitch 0.15s steps(1) infinite;\n}\n@keyframes glitch {\n 0% { filter: none; }\n 25% { filter: hue-rotate(90deg); transform: translate(-3px, 2px); }\n 50% { filter: saturate(3); transform: translate(3px, -2px); }\n 75% { filter: hue-rotate(-90deg); transform: translate(-2px, -1px); }\n}", "technologies": [ "CSS" ], "difficulty": "intermediate", "tags": [ "image", "glitch", "RGB-split", "hover", "VHS", "distortion", "CSS" ], "seen_in": "Cyberpunk-themed sites, music artist portfolios, Awwwards winners", "seen_in_url": "https://css-tricks.com/glitch-effect-text-images-svg/" }, { "id": "parallax-depth-map", "name_en": "Parallax Depth Map (2D to 3D)", "name_he": "פרלקס מפת עומק", "category": "image-effects", "description": "תמונה דו-מימדית שמרגישה תלת-מימדית — תנועת העכבר מזיזה שכבות שונות במהירויות שונות על סמך מפת עומק. שכבות קרובות נעות יותר, רחוקות פחות.", "prompt": "Create a 2D-to-3D parallax depth effect on an image using multiple layers that shift at different speeds based on cursor position.\n\nMethod 1 — Multi-Layer CSS Parallax (no depth map needed):\n\nHTML:\n
\n
\n \"\"\n
\n
\n \"\"\n
\n
\n \"\"\n
\n
\n\nCSS:\n.parallax-scene {\n position: relative;\n width: 100%;\n aspect-ratio: 16/9;\n overflow: hidden;\n perspective: 1000px;\n}\n\n.parallax-layer {\n position: absolute;\n inset: -5%; /* extra space to allow movement */\n width: 110%;\n height: 110%;\n transition: transform 0.1s ease-out;\n will-change: transform;\n}\n\n.parallax-layer img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n\nJavaScript:\nconst scene = document.getElementById('parallaxScene');\nconst layers = scene.querySelectorAll('.parallax-layer');\n\nscene.addEventListener('mousemove', (e) => {\n const rect = scene.getBoundingClientRect();\n const x = (e.clientX - rect.left - rect.width / 2) / rect.width; // -0.5 to 0.5\n const y = (e.clientY - rect.top - rect.height / 2) / rect.height; // -0.5 to 0.5\n \n layers.forEach(layer => {\n const depth = parseFloat(layer.dataset.depth);\n const moveX = x * depth * 40; // max 40px shift\n const moveY = y * depth * 40;\n layer.style.transform = `translate(${moveX}px, ${moveY}px)`;\n });\n});\n\nscene.addEventListener('mouseleave', () => {\n layers.forEach(layer => {\n layer.style.transform = 'translate(0, 0)';\n });\n});\n\nMethod 2 — Depth Map Library:\nUse Javallax (github.com/alessandrofrancesconi/javallax) with an actual grayscale depth map image for real displacement-based 3D effect.", "code_hint": "scene.addEventListener('mousemove', (e) => {\n const rect = scene.getBoundingClientRect();\n const x = (e.clientX - rect.left) / rect.width - 0.5;\n const y = (e.clientY - rect.top) / rect.height - 0.5;\n layers.forEach(l => {\n const d = l.dataset.depth;\n l.style.transform = `translate(${x*d*40}px, ${y*d*40}px)`;\n });\n});", "technologies": [ "CSS", "JavaScript" ], "difficulty": "intermediate", "tags": [ "image", "parallax", "depth-map", "3D", "layers", "cursor", "mouse-tracking" ], "seen_in": "Apple product pages, gaming landing pages, creative portfolios", "seen_in_url": "https://github.com/alessandrofrancesconi/javallax" }, { "id": "duotone-color-treatment", "name_en": "Duotone Color Treatment", "name_he": "טיפול צבע דו-גוני", "category": "image-effects", "description": "תמונה שמומרת לגווני אפור ומצופה בשני צבעי מותג — צללים בצבע אחד, הבהרות בצבע אחר. יוצר מראה גרפי ואחיד לסט תמונות מגוון. CSS בלבד.", "prompt": "Apply a duotone color treatment to images using only CSS — convert photos to two brand colors for a consistent graphic look.\n\nMethod 1 — CSS Mix-Blend-Mode:\n.duotone-container {\n position: relative;\n overflow: hidden;\n border-radius: 12px;\n}\n\n.duotone-container img {\n display: block;\n width: 100%;\n filter: grayscale(1) contrast(1.2);\n}\n\n/* Shadow color layer */\n.duotone-container::before {\n content: '';\n position: absolute;\n inset: 0;\n background: #1a0533; /* dark purple for shadows */\n mix-blend-mode: color;\n z-index: 1;\n}\n\n/* Highlight color layer */\n.duotone-container::after {\n content: '';\n position: absolute;\n inset: 0;\n background: #06b6d4; /* cyan for highlights */\n mix-blend-mode: screen;\n z-index: 2;\n opacity: 0.6;\n}\n\nMethod 2 — CSS Filter Shortcut:\n.duotone-quick {\n filter: grayscale(1) sepia(1) hue-rotate(180deg) saturate(2);\n /* Adjust hue-rotate degrees to match brand color */\n}\n\nInteractive Hover Reveal:\n.duotone-container:hover img {\n filter: grayscale(0);\n transition: filter 0.6s ease;\n}\n.duotone-container:hover::before,\n.duotone-container:hover::after {\n opacity: 0;\n transition: opacity 0.6s ease;\n}\n\nPresets (change ::before background and ::after background):\n- Purple + Gold: #2d1b69 / #fbbf24\n- Navy + Coral: #0f172a / #fb923c\n- Forest + Lime: #064e3b / #a3e635\n- Slate + Pink: #1e293b / #f472b6", "code_hint": ".duotone img { filter: grayscale(1) contrast(1.2); }\n.duotone::before {\n content: ''; position: absolute; inset: 0;\n background: #1a0533; mix-blend-mode: color; z-index: 1;\n}\n.duotone::after {\n content: ''; position: absolute; inset: 0;\n background: #06b6d4; mix-blend-mode: screen; z-index: 2; opacity: 0.6;\n}\n/* Hover: reveal original */\n.duotone:hover img { filter: none; }\n.duotone:hover::before, .duotone:hover::after { opacity: 0; }", "technologies": [ "CSS" ], "difficulty": "beginner", "tags": [ "image", "duotone", "color", "mix-blend-mode", "filter", "brand", "CSS-only" ], "seen_in": "Spotify playlists, Squarespace templates, marketing landing pages", "seen_in_url": "https://duotone.shapefactory.co/" }, { "id": "cinematic-lightbox-gallery", "name_en": "Cinematic Lightbox Gallery", "name_he": "גלריית לייטבוקס קולנועית", "category": "image-effects", "description": "גלריית תמונות שבלחיצה על תמונה נפתחת בהגדלה קולנועית — רקע מתעמעם, התמונה מתרחבת בצורה חלקה ממיקומה המקורי, עם ניווט חצים ותמיכה במקלדת.", "prompt": "Build a cinematic lightbox gallery with smooth image expansion from thumbnail position, keyboard navigation, and swipe support.\n\nHTML:\n
\n \n \n \n
\n\n
\n
\n \n \n \n \n
1 / 6
\n
\n\nCSS:\n.lightbox {\n position: fixed; inset: 0;\n z-index: 9999;\n display: flex; align-items: center; justify-content: center;\n opacity: 0; pointer-events: none;\n transition: opacity 0.3s ease;\n}\n.lightbox.open { opacity: 1; pointer-events: auto; }\n\n.lightbox-backdrop {\n position: absolute; inset: 0;\n background: rgba(0, 0, 0, 0.92);\n backdrop-filter: blur(8px);\n}\n\n.lightbox-image {\n position: relative;\n max-width: 90vw;\n max-height: 85vh;\n object-fit: contain;\n border-radius: 8px;\n z-index: 1;\n transform: scale(0.9);\n transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);\n}\n.lightbox.open .lightbox-image { transform: scale(1); }\n\n/* Navigation arrows */\n.lightbox-prev, .lightbox-next {\n position: absolute; top: 50%;\n transform: translateY(-50%);\n background: rgba(255,255,255,0.1);\n border: none; color: white;\n width: 48px; height: 48px;\n border-radius: 50%;\n font-size: 20px;\n cursor: pointer;\n z-index: 2;\n transition: background 0.2s;\n}\n.lightbox-prev { left: 16px; }\n.lightbox-next { right: 16px; }\n.lightbox-prev:hover, .lightbox-next:hover { background: rgba(255,255,255,0.2); }\n\nJavaScript:\nlet currentIndex = 0;\nconst thumbs = document.querySelectorAll('.gallery-thumb');\n\nthumbs.forEach((thumb, i) => {\n thumb.addEventListener('click', () => openLightbox(i));\n});\n\nfunction openLightbox(index) {\n currentIndex = index;\n const src = thumbs[index].dataset.full || thumbs[index].src;\n lightboxImg.src = src;\n lightbox.classList.add('open');\n document.body.style.overflow = 'hidden';\n updateCounter();\n}\n\n// Keyboard: left/right arrows, Escape to close\ndocument.addEventListener('keydown', (e) => {\n if (!lightbox.classList.contains('open')) return;\n if (e.key === 'ArrowRight') navigate(1);\n if (e.key === 'ArrowLeft') navigate(-1);\n if (e.key === 'Escape') closeLightbox();\n});\n\nfunction navigate(dir) {\n currentIndex = (currentIndex + dir + thumbs.length) % thumbs.length;\n lightboxImg.src = thumbs[currentIndex].dataset.full || thumbs[currentIndex].src;\n updateCounter();\n}", "code_hint": "function openLightbox(index) {\n currentIndex = index;\n lightboxImg.src = thumbs[index].dataset.full || thumbs[index].src;\n lightbox.classList.add('open');\n}\ndocument.addEventListener('keydown', (e) => {\n if (e.key === 'ArrowRight') navigate(1);\n if (e.key === 'ArrowLeft') navigate(-1);\n if (e.key === 'Escape') closeLightbox();\n});", "technologies": [ "CSS", "JavaScript" ], "difficulty": "intermediate", "tags": [ "image", "lightbox", "gallery", "cinematic", "keyboard", "modal", "fullscreen" ], "seen_in": "ARES-X Mars Gallery, Unsplash, photography portfolio sites", "seen_in_url": "https://unsplash.com" }, { "id": "particle-constellation-field", "name_en": "Particle Constellation Field", "name_he": "שדה כוכבים מחובר", "category": "canvas-effects", "description": "רשת של נקודות צפות על Canvas שמתחברות בקווים כשהן קרובות אחת לשנייה. תזוזת העכבר דוחפת את החלקיקים הקרובים ויוצרת אפקט אינטראקטיבי של קונסטלציה חיה.", "prompt": "Create a particle constellation field on an HTML5 Canvas that fills the viewport.\nSpawn 120 particles at random positions with random velocities (0.2-0.8px/frame).\nOn each requestAnimationFrame:\n1. Move particles and wrap them around edges.\n2. For every pair of particles within 150px distance, draw a line between them with opacity inversely proportional to distance (closer = more opaque).\n3. On mousemove, push particles within 100px radius away from the cursor with a gentle force.\n4. Draw each particle as a small circle (radius 2px) with rgba(255,255,255,0.7).\n5. Lines should be rgba(255,255,255, 1 - distance/150).\nBackground: #0a0a1a. Cap DPR at 2 for performance.\nInclude resize handler with debounce. Mobile: reduce to 60 particles.", "code_hint": "const canvas = document.getElementById('constellation');\nconst ctx = canvas.getContext('2d');\nconst particles = Array.from({length: 120}, () => ({\n x: Math.random() * w, y: Math.random() * h,\n vx: (Math.random() - 0.5) * 0.8,\n vy: (Math.random() - 0.5) * 0.8\n}));\n\nfunction animate() {\n ctx.clearRect(0, 0, w, h);\n particles.forEach(p => { /* move & wrap */ });\n // Draw connections within 150px\n for (let i = 0; i < particles.length; i++)\n for (let j = i+1; j < particles.length; j++) {\n const d = dist(particles[i], particles[j]);\n if (d < 150) {\n ctx.strokeStyle = `rgba(255,255,255,${1 - d/150})`;\n ctx.beginPath();\n ctx.moveTo(particles[i].x, particles[i].y);\n ctx.lineTo(particles[j].x, particles[j].y);\n ctx.stroke();\n }\n }\n requestAnimationFrame(animate);\n}", "technologies": [ "Canvas", "JavaScript" ], "difficulty": "intermediate", "tags": [ "canvas", "particles", "constellation", "interactive", "network", "mousemove" ], "seen_in": "Particle.js backgrounds, many SaaS landing pages", "seen_in_url": "https://vincentgarreau.com/particles.js/" }, { "id": "confetti-burst", "name_en": "Confetti Burst", "name_he": "פיצוץ קונפטי", "category": "canvas-effects", "description": "התפוצצות של חלקיקי קונפטי צבעוניים שנופלים עם גרביטציה וסיבוב. משתמשים ב-Canvas 2D כדי לצייר מלבנים ועיגולים סובבים עם פיזיקת נפילה ריאליסטית.", "prompt": "Build a confetti burst celebration effect using HTML5 Canvas.\nOn trigger (button click or event):\n1. Spawn 150 confetti particles from the center-top of the viewport.\n2. Each particle: random color from palette ['#ff577f','#ff884b','#ffd384','#fff9b0','#7c3aed','#06b6d4'], random shape (rectangle or circle), random size (6-12px).\n3. Physics per particle: initial velocity (random angle upward, speed 8-18), gravity 0.15 per frame, air resistance 0.99 friction, random spin speed (rotationDelta -0.15 to 0.15 radians/frame).\n4. Draw rectangles with ctx.rotate() for tumbling effect.\n5. Fade out alpha when y > canvas.height * 0.8.\n6. Remove particles when alpha < 0.01 or y > canvas.height + 20.\n7. Stop animation loop when all particles are gone.\nCanvas overlays the page with pointer-events: none and position: fixed.", "code_hint": "function burstConfetti() {\n const particles = Array.from({length: 150}, () => ({\n x: canvas.width/2, y: canvas.height * 0.3,\n vx: (Math.random()-0.5) * 16,\n vy: -Math.random() * 18 - 4,\n rotation: Math.random() * Math.PI * 2,\n rotDelta: (Math.random()-0.5) * 0.3,\n color: palette[Math.floor(Math.random()*palette.length)],\n size: 6 + Math.random() * 6, alpha: 1\n }));\n function frame() {\n ctx.clearRect(0,0,w,h);\n particles.forEach(p => {\n p.vy += 0.15; p.vx *= 0.99; p.vy *= 0.99;\n p.x += p.vx; p.y += p.vy;\n p.rotation += p.rotDelta;\n ctx.save(); ctx.translate(p.x,p.y);\n ctx.rotate(p.rotation);\n ctx.globalAlpha = p.alpha;\n ctx.fillStyle = p.color;\n ctx.fillRect(-p.size/2,-p.size/2,p.size,p.size);\n ctx.restore();\n });\n if (particles.some(p => p.alpha > 0.01)) requestAnimationFrame(frame);\n }\n frame();\n}", "technologies": [ "Canvas", "JavaScript" ], "difficulty": "intermediate", "tags": [ "canvas", "confetti", "celebration", "particles", "animation", "physics" ], "seen_in": "ARES-X Mission Complete ceremony, Notion task completion", "seen_in_url": "" }, { "id": "star-field-shooting-stars", "name_en": "Star Field with Shooting Stars", "name_he": "שדה כוכבים עם כוכבי שביט", "category": "canvas-effects", "description": "שמיים מלאי כוכבים מצוירים על Canvas בשלוש שכבות עומק עם parallax לפי גלילה ותזוזת עכבר. כל 5-12 שניות כוכב שביט חולף עם זנב גרדיאנט.", "prompt": "Create a full-viewport Canvas star field with 3-layer parallax and shooting stars.\nLayer setup:\n- Far layer: 100 stars, scroll drift 0.02, mouse drift 8px, size x0.6, alpha x0.25\n- Mid layer: 60 stars, scroll drift 0.06, mouse drift 28px, size x1.0, alpha x0.45\n- Near layer: 90 stars, scroll drift 0.14, mouse drift 60px, size x1.8, alpha x0.70\nEach star: random position, base radius 0.5-2px, subtle twinkle by oscillating alpha with Math.sin(time * twinkleSpeed + offset).\nShooting stars: check if currentTime - lastShoot > 5000 + Math.random()*7000. Draw as a linear gradient stroke from white to orange to transparent along the trail. Add a radial glow at the head (4px radius, white center fading to transparent).\nOn mousemove: shift each layer by mouse offset * layer drift factor.\nOn scroll: shift each layer vertically by scrollY * layer scroll drift.\nPerformance: cap DPR at 1 on mobile / 1.5 on desktop. Max 250 stars total desktop, 100 mobile.", "code_hint": "const layers = [\n { stars: [], drift: 0.02, mouseDrift: 8, sizeMult: 0.6, alphaMult: 0.25 },\n { stars: [], drift: 0.06, mouseDrift: 28, sizeMult: 1.0, alphaMult: 0.45 },\n { stars: [], drift: 0.14, mouseDrift: 60, sizeMult: 1.8, alphaMult: 0.70 }\n];\n// Shooting star drawn with:\nconst grad = ctx.createLinearGradient(sx, sy, sx-dx*30, sy-dy*30);\ngrad.addColorStop(0, 'rgba(255,255,255,0.9)');\ngrad.addColorStop(0.4, 'rgba(255,160,60,0.6)');\ngrad.addColorStop(1, 'rgba(255,100,20,0)');", "technologies": [ "Canvas", "JavaScript" ], "difficulty": "intermediate", "tags": [ "canvas", "stars", "parallax", "shooting-star", "space", "background" ], "seen_in": "ARES-X Star Field background", "seen_in_url": "" }, { "id": "procedural-landscape", "name_en": "Procedural Landscape", "name_he": "נוף פרוצדורלי", "category": "canvas-effects", "description": "נוף שנוצר פרוצדורלית על Canvas באמצעות שכבות של הרים עם parallax. כל שכבה נוצרת עם פונקציית noise שיוצרת קו אופק אורגני. צבעים משתנים לפי עומק.", "prompt": "Build a procedural landscape generator on HTML5 Canvas.\nCreate 5 mountain layers drawn back-to-front with parallax depth:\n- Layer 1 (farthest): color #1a1a2e, height amplitude 80px, scroll speed 0.1\n- Layer 2: color #16213e, amplitude 120px, speed 0.2\n- Layer 3: color #0f3460, amplitude 160px, speed 0.35\n- Layer 4: color #533483, amplitude 130px, speed 0.5\n- Layer 5 (closest): color #2b1055, amplitude 100px, speed 0.7\nFor each layer, generate the horizon line using simplex noise or chained Math.sin() with 3 octaves: y = baseY + sin(x*freq1)*amp1 + sin(x*freq2)*amp2 + sin(x*freq3)*amp3.\nFill from the horizon line down to canvas bottom.\nOn mousemove: shift each layer horizontally by mouse offset * layer speed.\nAdd a gradient sky from #0a0a2e at top to #1a1a3e at horizon.\nOptional: add tiny star dots in the sky area. Animate a slow color cycle on the sky gradient to simulate dawn/dusk.", "code_hint": "function drawLayer(ctx, baseY, amplitude, color, offset) {\n ctx.fillStyle = color;\n ctx.beginPath();\n ctx.moveTo(0, canvas.height);\n for (let x = 0; x <= canvas.width; x++) {\n const y = baseY + Math.sin((x + offset) * 0.003) * amplitude * 0.5\n + Math.sin((x + offset) * 0.007) * amplitude * 0.3\n + Math.sin((x + offset) * 0.015) * amplitude * 0.2;\n ctx.lineTo(x, y);\n }\n ctx.lineTo(canvas.width, canvas.height);\n ctx.closePath();\n ctx.fill();\n}", "technologies": [ "Canvas", "JavaScript" ], "difficulty": "intermediate", "tags": [ "canvas", "procedural", "landscape", "parallax", "generative", "mountains" ], "seen_in": "ARES-X Window to Mars porthole canvas", "seen_in_url": "" }, { "id": "flow-field-particles", "name_en": "Flow Field Particles", "name_he": "חלקיקים בשדה זרימה", "category": "canvas-effects", "description": "חלקיקים שזורמים לפי שדה וקטורים שנוצר מ-Perlin noise. כל חלקיק משאיר שובל שקוף שיוצר דפוסים אורגניים מרהיבים. תמונה חיה שמתפתחת כל הזמן.", "prompt": "Create a flow field particle system on HTML5 Canvas.\n1. Build a grid of angle vectors using Perlin noise (or simplex noise). Grid cell size: 20px. For each cell: angle = noise(col * 0.05, row * 0.05, time * 0.001) * TWO_PI * 2.\n2. Spawn 1000 particles at random positions. Each particle has: x, y, vx, vy, speed (1-3), maxSpeed (3), color.\n3. On each frame: for each particle, look up the grid cell it's in, get the angle, create a force vector (cos(angle), sin(angle)), add to velocity, clamp to maxSpeed, update position.\n4. Draw a 1px line from old position to new position with ctx.strokeStyle = particle color at low alpha (0.05-0.15).\n5. Do NOT clear the canvas each frame — the trails accumulate to form organic patterns.\n6. Every 300 frames, apply a very faint rectangle fill (rgba(10,10,26,0.01)) to slowly fade old trails.\n7. Wrap particles around canvas edges.\n8. Color palette: pick from ['#7c3aed','#06b6d4','#f59e0b','#ec4899','#10b981'] based on particle index.\nFor the noise function, use a simple 2D Perlin noise implementation (include it inline, ~40 lines).", "code_hint": "// Simplified noise-based angle lookup\nfunction getAngle(x, y, time) {\n const col = Math.floor(x / cellSize);\n const row = Math.floor(y / cellSize);\n return noise(col * 0.05, row * 0.05, time * 0.001) * Math.PI * 4;\n}\n\nfunction animate() {\n // Don't clear — trails accumulate\n particles.forEach(p => {\n const angle = getAngle(p.x, p.y, frame);\n p.vx += Math.cos(angle) * 0.3;\n p.vy += Math.sin(angle) * 0.3;\n p.vx = clamp(p.vx, -p.maxSpeed, p.maxSpeed);\n p.vy = clamp(p.vy, -p.maxSpeed, p.maxSpeed);\n const ox = p.x, oy = p.y;\n p.x += p.vx; p.y += p.vy;\n ctx.strokeStyle = p.color;\n ctx.globalAlpha = 0.08;\n ctx.beginPath(); ctx.moveTo(ox,oy); ctx.lineTo(p.x,p.y); ctx.stroke();\n });\n requestAnimationFrame(animate);\n}", "technologies": [ "Canvas", "JavaScript" ], "difficulty": "advanced", "tags": [ "canvas", "flow-field", "particles", "generative", "perlin-noise", "creative-coding" ], "seen_in": "Creative coding showcases, Tyler Hobbs generative art", "seen_in_url": "https://tylerxhobbs.com/" }, { "id": "canvas-scratch-card", "name_en": "Canvas Scratch-Card Reveal", "name_he": "כרטיס גירוד גילוי", "category": "canvas-effects", "description": "אפקט כרטיס גירוד שמגלה תוכן מתחת. Canvas מכסה את התוכן בשכבה אפורה והמשתמש מגרד עם העכבר כדי לחשוף את מה שמתחת. משתמש ב-globalCompositeOperation.", "prompt": "Create a scratch card reveal effect using HTML5 Canvas.\nStructure: a container div with hidden content (image, text, or coupon code) behind a Canvas overlay.\n1. Fill the canvas with a \"scratch layer\" — a solid color (#c0c0c0) or a gradient resembling a metallic scratch card.\n2. On mousedown/touchstart: set isScratching = true.\n3. On mousemove/touchmove while scratching: draw circles at the cursor position using ctx.globalCompositeOperation = 'destination-out' with radius 25px. This erases the scratch layer, revealing the content beneath.\n4. Track scratch percentage: every 500ms, use ctx.getImageData() to count transparent pixels vs total pixels. When scratch percentage > 60%, automatically reveal the rest with a fade-out animation on the canvas.\n5. Add a sparkle particle effect at the scratch point for visual feedback.\n6. Support both mouse and touch events. Prevent page scrolling while scratching on mobile.\n7. Canvas should have pointer-events: auto, content below has pointer-events: none until revealed.\nAdd a \"Scratch to reveal!\" text overlay that fades out once scratching begins.", "code_hint": "canvas.addEventListener('mousemove', (e) => {\n if (!isScratching) return;\n const rect = canvas.getBoundingClientRect();\n const x = e.clientX - rect.left;\n const y = e.clientY - rect.top;\n ctx.globalCompositeOperation = 'destination-out';\n ctx.beginPath();\n ctx.arc(x, y, 25, 0, Math.PI * 2);\n ctx.fill();\n});\n\nfunction checkScratchPercent() {\n const data = ctx.getImageData(0,0,w,h).data;\n let transparent = 0;\n for (let i = 3; i < data.length; i += 4)\n if (data[i] === 0) transparent++;\n return transparent / (data.length / 4);\n}", "technologies": [ "Canvas", "JavaScript" ], "difficulty": "intermediate", "tags": [ "canvas", "scratch-card", "reveal", "interactive", "gamification", "touch" ], "seen_in": "E-commerce promotions, gamified landing pages", "seen_in_url": "" }, { "id": "particle-text-2", "name_en": "Particle Text", "name_he": "טקסט מחלקיקים", "category": "canvas-effects", "description": "טקסט שנוצר מאלפי נקודות קטנות. כשעוברים עם העכבר, הנקודות מתפזרות ואז חוזרות למקום. הטקסט מצויר על canvas נסתר ואז הפיקסלים נדגמים ליצירת מיקומי חלקיקים.", "prompt": "Build a particle text effect on HTML5 Canvas.\n1. Create an offscreen canvas. Draw the text 'YOUR BRAND' in bold 80px font (Impact or similar). Use ctx.getImageData() to scan the pixels.\n2. Sample every 4th pixel. Where alpha > 128, create a particle at that (x,y) position. Store as particle.homeX, particle.homeY.\n3. Initialize each particle at a random position offscreen.\n4. Animate particles toward their home position using easing: p.x += (p.homeX - p.x) * 0.05.\n5. On mousemove: for particles within 80px of the cursor, push them away with force inversely proportional to distance. Use: dx = p.x - mouseX; dy = p.y - mouseY; dist = sqrt(dx*dx+dy*dy); force = (80 - dist) / 80; p.x += dx/dist * force * 5.\n6. Each particle: radius 1.5px, color sampled from the original text color or use a gradient palette.\n7. Entrance animation: particles fly in from random positions over 2 seconds on page load.\n8. On resize: recalculate text layout and particle home positions.\nPerformance: limit to 3000 particles. Use requestAnimationFrame.", "code_hint": "// Sample text pixels\noffCtx.font = 'bold 80px Impact';\noffCtx.fillText('BRAND', 0, 80);\nconst imageData = offCtx.getImageData(0,0,offW,offH);\nconst particles = [];\nfor (let y = 0; y < offH; y += 4)\n for (let x = 0; x < offW; x += 4) {\n const i = (y * offW + x) * 4;\n if (imageData.data[i+3] > 128)\n particles.push({ homeX: x+offsetX, homeY: y+offsetY,\n x: Math.random()*w, y: Math.random()*h });\n }", "technologies": [ "Canvas", "JavaScript" ], "difficulty": "advanced", "tags": [ "canvas", "particles", "text", "interactive", "cursor", "typography" ], "seen_in": "Creative agency hero sections", "seen_in_url": "" }, { "id": "heat-map-visualization", "name_en": "Heat Map Visualization", "name_he": "ויזואליזציית מפת חום", "category": "canvas-effects", "description": "מפת חום אינטראקטיבית על Canvas שמראה אזורי עניין. כל לחיצה או תנועת עכבר מוסיפה נקודת חום עם גרדיאנט רדיאלי. הצבעים נעים מכחול לירוק לצהוב לאדום.", "prompt": "Create a real-time heat map visualization on HTML5 Canvas.\n1. Set up two canvases: one for the shadow heat data (grayscale) and one for the colored display.\n2. On mousemove (or on predefined data points): draw a radial gradient circle on the shadow canvas at the point — center white fading to transparent, radius 40px, using ctx.globalCompositeOperation = 'lighter' so overlapping areas intensify.\n3. On each frame, read the shadow canvas pixel data with getImageData(). For each pixel, map the grayscale intensity (0-255) to a color gradient:\n - 0-64: transparent to blue\n - 64-128: blue to green\n - 128-192: green to yellow\n - 192-255: yellow to red\n4. Write the colored pixels to the display canvas with putImageData().\n5. Optional: add a slow decay — each frame, draw a faint black rect (alpha 0.005) on the shadow canvas so old heat dissipates.\n6. Display with mix-blend-mode: multiply over the page content.\nInclude a toggle button to show/hide the heat map.", "code_hint": "// Draw heat point\nshadowCtx.globalCompositeOperation = 'lighter';\nconst grad = shadowCtx.createRadialGradient(x,y,0,x,y,40);\ngrad.addColorStop(0, 'rgba(255,255,255,0.6)');\ngrad.addColorStop(1, 'rgba(255,255,255,0)');\nshadowCtx.fillStyle = grad;\nshadowCtx.fillRect(x-40,y-40,80,80);\n\n// Color mapping\nfunction intensityToColor(val) {\n if (val < 64) return [0, 0, val*4, val*4];\n if (val < 128) return [0, (val-64)*4, 255-(val-64)*4, 200];\n if (val < 192) return [(val-128)*4, 255, 0, 220];\n return [255, 255-(val-192)*4, 0, 240];\n}", "technologies": [ "Canvas", "JavaScript" ], "difficulty": "advanced", "tags": [ "canvas", "heatmap", "data-visualization", "interactive", "analytics" ], "seen_in": "Analytics dashboards, UX research tools", "seen_in_url": "" }, { "id": "animated-counter-canvas", "name_en": "Animated Counter (Canvas)", "name_he": "מונה מונפש (Canvas)", "category": "canvas-effects", "description": "מספרים שמסתובבים כמו אודומטר — כל ספרה גוללת בנפרד כשהמספר משתנה. מצויר על Canvas עם clipping masks. הספרות עולות או יורדות בקצב שונה בהתאם לערך.", "prompt": "Build an animated odometer-style counter using HTML5 Canvas.\n1. Accept a target number (e.g., 14,392). Each digit has its own vertical scroll column.\n2. For each digit position: create a vertical strip of numbers 0-9 stacked. Animate the strip's Y offset to land on the target digit.\n3. Use easeOutExpo easing: progress = 1 - Math.pow(1 - t, 4) where t goes from 0 to 1 over 2 seconds.\n4. Stagger the start of each digit's animation — rightmost digit starts first, leftmost last (50ms stagger per digit). This creates a rolling effect from right to left.\n5. Clip each digit column with ctx.save() + ctx.beginPath() + ctx.rect() + ctx.clip() so only the visible digit shows.\n6. Font: bold 48px monospace, color white (#ffffff), rendered with ctx.fillText().\n7. Add a subtle vertical gradient mask at top and bottom of each digit slot (fade to background) for a 3D drum effect.\n8. Trigger on IntersectionObserver — animate from 0 to target when the counter enters the viewport.\n9. Add comma separators rendered as static text between digit groups.", "code_hint": "function animateDigit(ctx, x, targetDigit, progress) {\n ctx.save();\n ctx.beginPath();\n ctx.rect(x, 0, digitWidth, slotHeight);\n ctx.clip();\n const totalScroll = targetDigit * digitHeight;\n const currentY = totalScroll * easeOutExpo(progress);\n for (let n = 0; n <= 9; n++) {\n ctx.fillText(n.toString(), x + digitWidth/2, n * digitHeight - currentY + baseline);\n }\n ctx.restore();\n}", "technologies": [ "Canvas", "JavaScript" ], "difficulty": "intermediate", "tags": [ "canvas", "counter", "odometer", "animation", "numbers", "scroll-trigger" ], "seen_in": "ARES-X telemetry counters, SaaS metrics sections", "seen_in_url": "" }, { "id": "canvas-mini-game", "name_en": "Canvas Mini-Game", "name_he": "משחק מיני על Canvas", "category": "canvas-effects", "description": "משחקון אסטרואידים או תופס-חפצים פשוט שמוטמע בדף. שחקן שולט עם מקשים או מגע ומנסה לשרוד. כולל ניקוד, התנגשויות, ואפקטי פיצוץ.", "prompt": "Create an embedded Canvas mini-game (asteroid dodger) that fits in a 600x400 section of the page.\n1. Player: a small triangle ship at the bottom center, moves left/right with arrow keys or A/D. On mobile: touch left/right halves of the canvas.\n2. Asteroids: spawn from the top at random X positions every 800ms. Random sizes (15-35px radius), random speeds (2-5px/frame). Draw as irregular polygons (6-8 vertices with random radius offsets for rocky look).\n3. Collision detection: circle-to-circle (ship hitbox radius 12px). On collision: show explosion particles (orange/red), shake the canvas with CSS transform for 200ms, show 'GAME OVER' screen with score and 'Play Again' button.\n4. Score: +10 per asteroid that passes the bottom. Display in top-right corner.\n5. Progressive difficulty: every 30 seconds, reduce spawn interval by 100ms and increase max speed by 0.5.\n6. Background: dark gradient. Add scrolling star dots for motion feel.\n7. Game loop: 60fps requestAnimationFrame. Pause when tab is hidden (visibilitychange). Include ResizeObserver for responsive canvas.\n8. Wrap in a self-contained module — no global variables.", "code_hint": "class AsteroidGame {\n constructor(canvas) {\n this.ctx = canvas.getContext('2d');\n this.ship = { x: canvas.width/2, y: canvas.height - 40, r: 12 };\n this.asteroids = [];\n this.score = 0;\n this.spawnInterval = 800;\n }\n spawnAsteroid() {\n const vertices = Array.from({length: 7}, (_, i) => {\n const angle = (i / 7) * Math.PI * 2;\n const r = 20 + Math.random() * 15;\n return { x: Math.cos(angle)*r, y: Math.sin(angle)*r };\n });\n this.asteroids.push({ x: Math.random()*this.w, y: -30,\n speed: 2+Math.random()*3, vertices });\n }\n checkCollision(ship, ast) {\n return Math.hypot(ship.x-ast.x, ship.y-ast.y) < ship.r + 20;\n }\n}", "technologies": [ "Canvas", "JavaScript" ], "difficulty": "advanced", "tags": [ "canvas", "game", "interactive", "asteroid", "collision", "gamification", "easter-egg" ], "seen_in": "ARES-X hidden asteroid game", "seen_in_url": "" }, { "id": "webgl-fluid-simulation", "name_en": "Fluid Simulation", "name_he": "סימולציית נוזל", "category": "webgl-effects", "description": "סימולציית נוזלים בזמן אמת באמצעות WebGL. העכבר מזריק צבע ומהירות לנוזל, ומשוואות נאוויה-סטוקס מחושבות ב-GPU. האפקט הפופולרי ביותר ב-WebGL.", "prompt": "Implement an interactive WebGL fluid simulation based on the Navier-Stokes equations.\nUse WebGL2 with floating-point textures (EXT_color_buffer_float). Create these fragment shader passes:\n1. Advection — move dye and velocity fields along the velocity field using bilinear interpolation.\n2. Curl — compute vorticity (rotation) of the velocity field.\n3. Vorticity Confinement — amplify small vortices to prevent over-damping.\n4. Divergence — compute how much fluid is compressing/expanding.\n5. Pressure — iteratively solve Poisson equation (20-40 Jacobi iterations).\n6. Gradient Subtraction — make velocity field divergence-free (incompressible).\n7. Dye pass — inject color splats.\nPointer interaction: on mousemove, inject a velocity splat (direction = mouse delta, radius 100px) and a color splat (random vibrant hue).\nAuto-splats: when idle for 3 seconds, add random splats to keep it alive.\nRender the dye texture to a fullscreen quad.\nCanvas fills viewport with position: fixed, pointer-events: all, z-index: -1.\nSliders panel: dissipation (0.97-1.0), vorticity (0-50), pressure iterations (10-60).\nFull-page background mode. Performance: simulate on a grid 1/4 the display resolution.", "code_hint": "// Simplified shader pipeline structure\nconst programs = {\n advection: createProgram(baseVert, advectionFrag),\n curl: createProgram(baseVert, curlFrag),\n vorticity: createProgram(baseVert, vorticityFrag),\n divergence: createProgram(baseVert, divergenceFrag),\n pressure: createProgram(baseVert, pressureFrag),\n gradSub: createProgram(baseVert, gradSubFrag),\n splat: createProgram(baseVert, splatFrag),\n display: createProgram(baseVert, displayFrag)\n};\n// Each frame: advect velocity, advect dye, compute curl,\n// apply vorticity, compute divergence, solve pressure (N iterations),\n// subtract gradient, render dye", "technologies": [ "WebGL" ], "difficulty": "advanced", "tags": [ "webgl", "fluid", "navier-stokes", "simulation", "interactive", "gpu", "shader" ], "seen_in": "Pavel Dobryakov's WebGL Fluid Simulation (16K+ GitHub stars)", "seen_in_url": "https://paveldogreat.github.io/WebGL-Fluid-Simulation/" }, { "id": "noise-blob", "name_en": "Noise Blob (Organic Morphing)", "name_he": "בועת רעש אורגנית", "category": "webgl-effects", "description": "כדור תלת-מימדי שמשתנה אורגנית כמו בועה חיה. vertex shader מוסיף 3D noise לכל קודקוד שמשנה את הרדיוס לאורך זמן. תאורה דינמית יוצרת מראה של חומר נוזלי.", "prompt": "Create a WebGL organic noise blob using Three.js.\n1. Create an IcosahedronGeometry with radius 2 and detail 64 (high poly for smooth deformation).\n2. Write a custom ShaderMaterial:\n - Vertex shader: displace each vertex along its normal using 3D simplex noise. displacement = normal * noise3D(position * noiseScale + time * speed) * amplitude. Use 3 octaves of noise for organic detail.\n - Fragment shader: compute Fresnel effect (brighter at edges). Use a gradient from deep purple (#7c3aed) at center to cyan (#06b6d4) at edges based on Fresnel factor.\n3. Uniforms: time (float, auto-increment), noiseScale (2.0), amplitude (0.4), speed (0.3).\n4. Lighting: add a directional light and ambient light for depth.\n5. Camera: perspective at z=5, slowly orbit the blob using sin/cos on time.\n6. Background: transparent or dark (#0a0a0f).\n7. Renderer: set alpha: true, antialias: true. Canvas fills a hero section.\n8. On mousemove: tilt the blob slightly toward the cursor (max 15 degrees).\n9. Add postprocessing UnrealBloomPass for a glowing edge effect.\nInclude the simplex noise GLSL function inline in the vertex shader.", "code_hint": "const material = new THREE.ShaderMaterial({\n vertexShader: `\n uniform float time;\n varying vec3 vNormal;\n // Include snoise3D function here\n void main() {\n float n = snoise(position * 2.0 + time * 0.3) * 0.4;\n vec3 newPos = position + normal * n;\n vNormal = normalMatrix * normal;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(newPos, 1.0);\n }`,\n fragmentShader: `\n varying vec3 vNormal;\n void main() {\n float fresnel = pow(1.0 - abs(dot(vNormal, vec3(0,0,1))), 2.0);\n vec3 color = mix(vec3(0.486,0.228,0.929), vec3(0.024,0.714,0.831), fresnel);\n gl_FragColor = vec4(color, 1.0);\n }`\n});", "technologies": [ "Three.js", "WebGL" ], "difficulty": "advanced", "tags": [ "webgl", "blob", "noise", "organic", "shader", "three.js", "morphing" ], "seen_in": "Stripe, Linear, creative agency hero sections", "seen_in_url": "https://www.ericchung.dev/posts/webgl-blobs" }, { "id": "image-ripple-distortion", "name_en": "Image Ripple Distortion", "name_he": "אפקט אדוות על תמונה", "category": "webgl-effects", "description": "תמונה שמתעוותת באדוות במקום שהעכבר נוגע בה. WebGL fragment shader מזיז UV coordinates לפי פונקציית sin שמתרחקת מנקודת העכבר, כמו זריקת אבן לבריכה.", "prompt": "Create a WebGL image ripple distortion effect on hover.\nSetup: load an image as a WebGL texture on a fullscreen quad.\nFragment shader:\n1. Calculate distance from current UV to mouse UV position.\n2. Apply sinusoidal displacement to UV coordinates:\n float ripple = sin(dist * frequency - time * speed) * amplitude * smoothstep(maxRadius, 0.0, dist);\n vec2 distortedUV = uv + normalize(uv - mouseUV) * ripple;\n3. Sample the texture at distortedUV.\n4. Uniforms: mouseUV (vec2, normalized 0-1), time (float), frequency (20.0), amplitude (0.02), speed (4.0), maxRadius (0.4).\n5. On mousemove: update mouseUV uniform. On mouseleave: animate amplitude to 0 over 1 second.\n6. On click: trigger a single strong ripple (amplitude spike to 0.06 then decay).\n7. Add a subtle chromatic aberration: sample R, G, B channels at slightly different offsets along the ripple direction.\nRenderer: WebGL2 context, canvas overlays the image. Fallback: show static image if WebGL unavailable.", "code_hint": "// Fragment shader core\nprecision highp float;\nuniform sampler2D uTexture;\nuniform vec2 uMouse;\nuniform float uTime;\nuniform float uAmplitude;\nvarying vec2 vUv;\n\nvoid main() {\n float dist = distance(vUv, uMouse);\n float ripple = sin(dist * 20.0 - uTime * 4.0) * uAmplitude * smoothstep(0.4, 0.0, dist);\n vec2 dir = normalize(vUv - uMouse);\n vec2 distortedUV = vUv + dir * ripple;\n // Chromatic aberration\n float r = texture2D(uTexture, distortedUV + dir * ripple * 0.5).r;\n float g = texture2D(uTexture, distortedUV).g;\n float b = texture2D(uTexture, distortedUV - dir * ripple * 0.5).b;\n gl_FragColor = vec4(r, g, b, 1.0);\n}", "technologies": [ "WebGL", "JavaScript" ], "difficulty": "advanced", "tags": [ "webgl", "ripple", "distortion", "hover", "shader", "image", "interactive" ], "seen_in": "Codrops tutorials, creative portfolio sites", "seen_in_url": "https://tympanus.net/codrops/" }, { "id": "ray-marching-shader", "name_en": "Ray Marching Shader", "name_he": "שיידר Ray Marching", "category": "webgl-effects", "description": "סצנה תלת-מימדית שנוצרת לחלוטין בתוך fragment shader באמצעות ray marching ו-signed distance functions. אין משולשים — רק מתמטיקה. יוצר צורות אורגניות מדהימות.", "prompt": "Create a ray marching shader scene rendered on a fullscreen WebGL quad.\nFragment shader implementation:\n1. Ray setup: for each pixel, compute ray origin (camera position) and ray direction based on UV and field of view.\n2. SDF scene: combine primitives using smooth min (smin):\n - A sphere at origin: length(p) - 1.0\n - A wavy floor: p.y + 1.5 + sin(p.x*2.0+time)*0.1 + sin(p.z*2.0+time*0.7)*0.1\n - A torus orbiting the sphere: use rotation matrix\n3. Ray march loop: max 100 steps, step along ray by the SDF distance. Break when distance < 0.001 or total distance > 50.0.\n4. Normal calculation: use central differences (sample SDF at position +/- small epsilon on each axis).\n5. Lighting: one directional light + ambient. Compute diffuse + specular (Blinn-Phong). Add soft shadows by marching a secondary ray toward the light.\n6. Material: color based on which object was hit. Sphere = metallic purple, floor = dark gray with grid pattern, torus = cyan.\n7. Background: dark gradient.\n8. Animate: rotate camera slowly around the scene. Pulse the sphere radius with sin(time).\nUniforms: time, resolution, mouse (for optional camera orbit control).\nCanvas fills the hero section. Include a play/pause toggle.", "code_hint": "// Core SDF + ray march\nfloat smin(float a, float b, float k) {\n float h = clamp(0.5 + 0.5*(b-a)/k, 0.0, 1.0);\n return mix(b, a, h) - k*h*(1.0-h);\n}\nfloat sceneSDF(vec3 p) {\n float sphere = length(p) - 1.0;\n float floor = p.y + 1.5;\n return smin(sphere, floor, 0.5);\n}\nvec3 march(vec3 ro, vec3 rd) {\n float t = 0.0;\n for (int i = 0; i < 100; i++) {\n float d = sceneSDF(ro + rd * t);\n if (d < 0.001) break;\n t += d;\n if (t > 50.0) break;\n }\n return ro + rd * t;\n}", "technologies": [ "WebGL" ], "difficulty": "advanced", "tags": [ "webgl", "ray-marching", "sdf", "shader", "procedural", "3d", "glsl" ], "seen_in": "Shadertoy showcases, award-winning creative sites", "seen_in_url": "https://www.shadertoy.com/" }, { "id": "webgl-gradient-stripe", "name_en": "WebGL Gradient (Stripe-style)", "name_he": "גרדיאנט WebGL (בסגנון Stripe)", "category": "webgl-effects", "description": "הרקע האייקוני של Stripe — גרדיאנט mesh מונפש ב-WebGL שזורם באיטיות עם שכבות של Simplex noise. הקנבס מוטה ב-skewY ויוצר צורה אלכסונית. ביצועים מעולים כי ה-GPU עושה הכל.", "prompt": "Recreate the Stripe-style animated WebGL mesh gradient background.\n1. Create a WebGL canvas that fills a section (or full viewport).\n2. Fragment shader: layer 4 color stops that morph positions over time using Fractal Brownian Motion (FBM):\n - FBM function: 4 octaves of simplex noise, lacunarity 2.0, persistence 0.5.\n - Define 4 gradient colors: e.g., #7c3aed (purple), #06b6d4 (cyan), #f59e0b (amber), #ec4899 (pink).\n - Each color's position shifts over time: pos_i = basePos_i + fbm(uv * scale + time * speed_i) * amplitude.\n - Blend colors using smoothstep between positions.\n3. Apply the Stripe skew: wrap the canvas container in a div with CSS transform: skewY(-12deg) and overflow: hidden. This creates the signature diagonal edges.\n4. The canvas element itself renders a normal rectangle — the CSS transform handles the shape.\n5. Uniforms: time (auto), colors (vec3 array), resolution.\n6. Performance: this is just one fragment shader call per pixel — extremely fast. The 10kb minigl approach Stripe uses.\n7. Optional: add a subtle noise grain overlay in the shader for texture.\nCanvas should be position: absolute behind page content.", "code_hint": "// Fragment shader core\nfloat fbm(vec2 p) {\n float val = 0.0, amp = 0.5;\n for (int i = 0; i < 4; i++) {\n val += amp * snoise(p);\n p *= 2.0; amp *= 0.5;\n }\n return val;\n}\nvoid main() {\n vec2 uv = gl_FragCoord.xy / resolution;\n float n1 = fbm(uv * 3.0 + time * 0.1);\n float n2 = fbm(uv * 2.0 + time * 0.15 + 100.0);\n vec3 c1 = mix(purple, cyan, smoothstep(-0.3, 0.3, n1));\n vec3 c2 = mix(amber, pink, smoothstep(-0.2, 0.4, n2));\n vec3 color = mix(c1, c2, smoothstep(0.3, 0.7, uv.y + n1*0.2));\n gl_FragColor = vec4(color, 1.0);\n}\n/* Container CSS: transform: skewY(-12deg); overflow: hidden; */", "technologies": [ "WebGL", "CSS" ], "difficulty": "advanced", "tags": [ "webgl", "gradient", "stripe", "mesh", "fbm", "noise", "background" ], "seen_in": "Stripe.com homepage", "seen_in_url": "https://stripe.com" }, { "id": "displacement-hover", "name_en": "Displacement Hover Effect", "name_he": "אפקט עיוות בריחוף", "category": "webgl-effects", "description": "שתי תמונות שמתחלפות עם אפקט displacement כשמרחפים. תמונת displacement map בשחור-לבן שולטת על כיוון העיוות. מעבר חלק ואורגני בין שתי תמונות.", "prompt": "Create a WebGL displacement hover transition between two images.\nSetup with Three.js:\n1. Create a PlaneGeometry fullscreen quad with a custom ShaderMaterial.\n2. Load 3 textures: image1 (current), image2 (hover), displacementMap (a black-and-white pattern — use a cloud/smoke texture).\n3. Fragment shader:\n - Uniform float progress (0 to 1, animated on hover).\n - Sample the displacement map: float disp = texture2D(dispMap, vUv).r;\n - Offset UV for each image in opposite directions:\n vec2 uv1 = vUv + disp * progress * vec2(0.2, 0.0);\n vec2 uv2 = vUv - disp * (1.0 - progress) * vec2(0.2, 0.0);\n - Mix: vec4 color = mix(texture2D(tex1, uv1), texture2D(tex2, uv2), progress);\n4. On mouseenter: animate progress from 0 to 1 over 1.2 seconds with easeInOut.\n5. On mouseleave: animate progress from 1 to 0.\n6. Use GSAP to animate the uniform: gsap.to(material.uniforms.progress, { value: 1, duration: 1.2, ease: 'power2.inOut' }).\n7. Scale the displacement intensity during animation: peak at progress=0.5, zero at 0 and 1.\nGreat for portfolio thumbnails and image galleries.", "code_hint": "const material = new THREE.ShaderMaterial({\n uniforms: {\n tex1: { value: texture1 },\n tex2: { value: texture2 },\n dispMap: { value: dispTexture },\n progress: { value: 0.0 }\n },\n fragmentShader: `\n uniform sampler2D tex1, tex2, dispMap;\n uniform float progress;\n varying vec2 vUv;\n void main() {\n float disp = texture2D(dispMap, vUv).r;\n float intensity = progress * (1.0 - progress) * 4.0;\n vec2 uv1 = vUv + disp * intensity * vec2(0.2, 0);\n vec2 uv2 = vUv - disp * intensity * vec2(0.2, 0);\n gl_FragColor = mix(texture2D(tex1, uv1), texture2D(tex2, uv2), progress);\n }`\n});", "technologies": [ "Three.js", "WebGL", "JavaScript" ], "difficulty": "advanced", "tags": [ "webgl", "displacement", "hover", "transition", "image", "shader", "three.js" ], "seen_in": "Portfolio sites, Codrops hover effect demos", "seen_in_url": "https://tympanus.net/codrops/" }, { "id": "shader-transition", "name_en": "Shader Transition", "name_he": "מעבר שיידר", "category": "webgl-effects", "description": "מעברים בין תמונות או סקשנים באמצעות GLSL shaders מותאמים. במקום fade רגיל, התמונה נמסה, מתגלה דרך noise, או מתעוותת. GL Transitions מספקת עשרות מעברים מוכנים.", "prompt": "Build a WebGL shader-based image transition system using Three.js.\nCreate a carousel/slider where transitions between slides use custom GLSL fragment shaders.\nImplement 3 transition types:\n1. Noise dissolve: use simplex noise as a threshold mask. pixels where noise(uv) < progress show image2, otherwise image1. Add a bright edge (emission line) at the transition boundary.\n - Edge glow: if abs(noise - progress) < 0.02, output bright white/colored pixel.\n2. Directional wipe with wave edge: transition sweeps from left to right but the edge is a sine wave, not a straight line.\n - float edge = progress * 1.4 - 0.2 + sin(uv.y * 8.0) * 0.05;\n - mix based on step(uv.x, edge).\n3. Pixelate transition: during transition, progressively pixelate (reduce UV resolution), swap image at midpoint, then de-pixelate.\n - float size = mix(1.0, 40.0, progress*(1.0-progress)*4.0);\n - vec2 pixelUV = floor(uv * size) / size;\nAuto-play carousel: advance every 5 seconds. Navigation dots below.\nAnimate progress uniform from 0 to 1 over 1.5 seconds per transition.\nFallback: CSS crossfade if WebGL unavailable.", "code_hint": "// Noise dissolve transition\nfloat n = snoise(vUv * 4.0);\nfloat threshold = progress;\nfloat edge = smoothstep(threshold-0.02, threshold, n) - smoothstep(threshold, threshold+0.02, n);\nvec4 c1 = texture2D(tex1, vUv);\nvec4 c2 = texture2D(tex2, vUv);\nvec4 color = mix(c2, c1, step(threshold, n));\ncolor.rgb += vec3(1.0, 0.5, 0.1) * edge * 3.0; // glowing edge\ngl_FragColor = color;", "technologies": [ "Three.js", "WebGL" ], "difficulty": "advanced", "tags": [ "webgl", "shader", "transition", "carousel", "dissolve", "glsl" ], "seen_in": "GL Transitions project, Awwwards-winning galleries", "seen_in_url": "https://gl-transitions.com/" }, { "id": "postprocessing-bloom", "name_en": "Postprocessing Bloom", "name_he": "אפקט זוהר פוסט-פרוססינג", "category": "webgl-effects", "description": "אפקט bloom שגורם לאלמנטים בהירים לזהור החוצה כמו אור אמיתי. מבוסס על EffectComposer של Three.js — מחלץ פיקסלים בהירים, מטשטש אותם, ומשלב חזרה.", "prompt": "Add postprocessing bloom (glow) to a Three.js scene using EffectComposer.\n1. Set up a basic Three.js scene with:\n - Dark background (#0a0a0f)\n - Several glowing objects: neon rings (TorusGeometry), emissive spheres, glowing text\n - Objects should have MeshStandardMaterial with emissive color and emissiveIntensity > 1\n2. Create the postprocessing pipeline:\n - import { EffectComposer } from 'three/addons/postprocessing/EffectComposer'\n - import { RenderPass } from 'three/addons/postprocessing/RenderPass'\n - import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass'\n - const composer = new EffectComposer(renderer);\n - composer.addPass(new RenderPass(scene, camera));\n - const bloom = new UnrealBloomPass(new THREE.Vector2(w,h), strength, radius, threshold);\n - bloom.strength = 1.5 (glow intensity)\n - bloom.radius = 0.8 (spread of glow)\n - bloom.threshold = 0.2 (brightness cutoff — only bright areas glow)\n - composer.addPass(bloom);\n3. In the animation loop: call composer.render() instead of renderer.render().\n4. Selective bloom (optional): use layers to only bloom certain objects. Non-glowing objects render normally, glowing objects get the bloom treatment.\n5. Add dat.gui or simple sliders for strength, radius, threshold.\n6. Animate the emissive intensity of objects with Math.sin(time) for a pulsing glow.", "code_hint": "import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';\nimport { RenderPass } from 'three/addons/postprocessing/RenderPass.js';\nimport { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';\n\nconst composer = new EffectComposer(renderer);\ncomposer.addPass(new RenderPass(scene, camera));\nconst bloomPass = new UnrealBloomPass(\n new THREE.Vector2(window.innerWidth, window.innerHeight),\n 1.5, // strength\n 0.8, // radius\n 0.2 // threshold\n);\ncomposer.addPass(bloomPass);\n\n// In animation loop:\nfunction animate() {\n requestAnimationFrame(animate);\n composer.render(); // NOT renderer.render()\n}", "technologies": [ "Three.js", "WebGL" ], "difficulty": "intermediate", "tags": [ "webgl", "bloom", "glow", "postprocessing", "three.js", "neon", "emissive" ], "seen_in": "Gaming sites, cyberpunk-themed landing pages, product showcases", "seen_in_url": "" }, { "id": "3d-product-viewer", "name_en": "3D Product Viewer (Rotate on Scroll)", "name_he": "מציג מוצר תלת-מימדי (סיבוב בגלילה)", "category": "3d-effects", "description": "מודל תלת-מימדי של מוצר שמסתובב ומתרחק בזמן גלילה — בדיוק כמו דפי המוצרים של Apple. טוען קובץ GLTF ומפה את מיקום הגלילה לסיבוב ומצלמה.", "prompt": "Build an Apple-style 3D product scroll animation with Three.js and React Three Fiber.\n1. Load a GLTF/GLB 3D model using useGLTF() from @react-three/drei.\n2. Create a sticky container (height: 300vh, the 3D canvas is position: sticky at top: 0).\n3. Track scroll progress (0 to 1) within the sticky section using IntersectionObserver or a scroll handler: progress = scrollOffset / (sectionHeight - viewportHeight).\n4. Define 3 camera states keyed to scroll progress:\n - State 1 (0-0.33): camera faces front of product, model rotation Y = 0\n - State 2 (0.33-0.66): model rotates to show left side (Y = -PI/2), camera moves right\n - State 3 (0.66-1): model rotates to top/aerial view (X = -PI/4, Y = -PI), camera lifts up\n5. Interpolate between states using THREE.MathUtils.lerp for smooth transitions.\n6. Lighting: AmbientLight intensity 0.5, one DirectionalLight from upper-right. Add an HDRI environment map for realistic reflections using Environment from @react-three/drei.\n7. Background: black (#000000) or transparent.\n8. Add text overlays for each state that fade in/out based on progress.\n9. Performance: use draco compression for the GLTF, add Suspense with a loading spinner.\n10. Mobile: reduce model complexity, disable OrbitControls (scroll-only).", "code_hint": "import { Canvas } from '@react-three/fiber';\nimport { useGLTF, Environment } from '@react-three/drei';\n\nfunction Product({ scrollProgress }) {\n const { scene } = useGLTF('/model.glb');\n const rotY = THREE.MathUtils.lerp(0, -Math.PI, scrollProgress);\n const rotX = scrollProgress > 0.66\n ? THREE.MathUtils.lerp(0, -Math.PI/4, (scrollProgress-0.66)/0.34)\n : 0;\n return (\n \n );\n}\n\n// In JSX:\n
\n
\n \n \n \n \n \n
\n
", "technologies": [ "React Three Fiber", "Three.js", "Drei" ], "difficulty": "advanced", "tags": [ "3d", "product", "scroll", "gltf", "r3f", "apple-style", "three.js" ], "seen_in": "Apple.com product pages (iPhone, MacBook)", "seen_in_url": "https://apple.com/iphone" }, { "id": "floating-3d-objects", "name_en": "Floating 3D Objects", "name_he": "אובייקטים תלת-מימדיים מרחפים", "category": "3d-effects", "description": "אובייקטים גאומטריים תלת-מימדיים שמרחפים ומסתובבים באיטיות ברקע של דף. כל אובייקט נע במסלול שונה עם מהירות שונה, יוצר מראה של עולם צף.", "prompt": "Create a floating 3D objects background using React Three Fiber.\n1. Create 15-20 random geometric objects: mix of BoxGeometry, SphereGeometry, TorusGeometry, ConeGeometry, OctahedronGeometry.\n2. Each object: random position within a 20x20x20 cube, random scale (0.3-1.2), random rotation, random color from palette ['#7c3aed','#06b6d4','#f59e0b','#ec4899','#10b981'].\n3. Material: MeshStandardMaterial with roughness 0.3, metalness 0.7 for a polished look. Some objects wireframe, some solid.\n4. Animation per object (useFrame hook):\n - Float: y += Math.sin(time * speed + offset) * 0.3\n - Rotate: rotation.x += 0.003 * speedX, rotation.y += 0.005 * speedY\n - Each object has unique speed and offset values seeded at creation\n5. Lighting: soft AmbientLight + two colored PointLights (purple and cyan) positioned at opposite corners.\n6. Camera: fixed perspective, slight parallax on mousemove (shift camera position by mouse offset * 0.5).\n7. Depth of field: add a subtle fog (Fog near=10, far=25) to fade distant objects.\n8. Canvas positioned behind page content with z-index: -1, pointer-events: none.\n9. Performance: use instanced meshes if many objects share geometry. Limit to 20 on mobile.", "code_hint": "function FloatingObject({ geometry, position, color, speed }) {\n const ref = useRef();\n useFrame(({ clock }) => {\n const t = clock.getElapsedTime();\n ref.current.position.y = position[1] + Math.sin(t * speed + position[0]) * 0.4;\n ref.current.rotation.x += 0.003 * speed;\n ref.current.rotation.y += 0.005 * speed;\n });\n return (\n \n {geometry}\n \n \n );\n}", "technologies": [ "React Three Fiber", "Three.js" ], "difficulty": "intermediate", "tags": [ "3d", "floating", "objects", "background", "r3f", "animation", "geometric" ], "seen_in": "SaaS landing pages, tech product sites", "seen_in_url": "" }, { "id": "3d-text-material", "name_en": "3D Text with Material", "name_he": "טקסט תלת-מימדי עם חומר", "category": "3d-effects", "description": "טקסט תלת-מימדי אמיתי עם עומק, תאורה, והשתקפויות. משתמש ב-TextGeometry של Three.js או ברכיב Text3D של Drei. ניתן להוסיף חומרים מתכתיים, זכוכית, או ניאון.", "prompt": "Create extruded 3D text with metallic material using React Three Fiber and Drei.\n1. Use the component from @react-three/drei.\n2. Load a font JSON file (use Inter, Helvetica, or any typeface from three/examples/fonts/).\n3. Text content: 'WOW' or any brand name. Settings: size 2, height (extrusion depth) 0.5, curveSegments 12, bevelEnabled true, bevelThickness 0.05, bevelSize 0.02.\n4. Material: MeshStandardMaterial with:\n - color: '#7c3aed' (purple)\n - metalness: 0.9\n - roughness: 0.1\n - envMapIntensity: 1.5\n5. Add an HDRI environment map (Environment preset='city') for realistic reflections on the metallic surface.\n6. Center the text using
from Drei.\n7. Animation: slow rotation on Y axis (0.003 per frame). On mousemove: tilt text toward cursor (max 10 degrees on X and Y).\n8. Lighting: AmbientLight 0.3, SpotLight from above casting shadow on a ground plane.\n9. Optional: add UnrealBloomPass for glowing edges.\n10. Add a ground plane with MeshReflectorMaterial from Drei for a reflection effect beneath the text.", "code_hint": "import { Text3D, Center, Environment, MeshReflectorMaterial } from '@react-three/drei';\n\nfunction BrandText() {\n const ref = useRef();\n useFrame(({ clock }) => {\n ref.current.rotation.y = Math.sin(clock.getElapsedTime() * 0.3) * 0.1;\n });\n return (\n
\n \n WOW\n \n \n
\n );\n}", "technologies": [ "React Three Fiber", "Drei", "Three.js" ], "difficulty": "intermediate", "tags": [ "3d", "text", "metallic", "drei", "r3f", "typography", "extrusion" ], "seen_in": "Award-winning agency sites, product launches", "seen_in_url": "" }, { "id": "particle-sphere", "name_en": "Particle Sphere", "name_he": "כדור חלקיקים", "category": "3d-effects", "description": "כדור שמורכב מאלפי חלקיקים קטנים במקום משטח מלא. כל חלקיק מרחף על פני הכדור ומגיב לעכבר. ניתן לפוצץ את הכדור ולהרכיב אותו מחדש.", "prompt": "Build a 3D particle sphere using React Three Fiber with custom shaders.\n1. Generate 5000 points distributed on a sphere surface using the Fibonacci sphere algorithm:\n for i in 0..N: phi = acos(1 - 2*(i+0.5)/N); theta = PI * (1+sqrt(5)) * i;\n x = sin(phi)*cos(theta)*radius; y = sin(phi)*sin(theta)*radius; z = cos(phi)*radius;\n2. Store positions in a Float32Array and use BufferGeometry with a Points mesh.\n3. Custom ShaderMaterial:\n - Vertex shader: displace each point along its normal with noise based on time: newPos = pos + normalize(pos) * snoise(pos * 2.0 + time * 0.5) * 0.3\n - Add a uniform 'explode' (0.0 to 1.0): when active, push particles outward: newPos += normalize(pos) * explode * 5.0\n - Fragment shader: draw each point as a soft circle (smooth edge with distance from gl_PointCoord center). Color: mix purple and cyan based on Y position.\n4. Point size: 3.0 * (300.0 / length(mvPosition.xyz)) for perspective scaling.\n5. Mouse interaction: on hover, set explode to 0.3 (partial scatter). On click, animate explode to 1.0 then back to 0.0.\n6. Slow auto-rotation on Y axis.\n7. Add postprocessing bloom for glowing particles.", "code_hint": "// Fibonacci sphere point generation\nconst positions = new Float32Array(5000 * 3);\nfor (let i = 0; i < 5000; i++) {\n const phi = Math.acos(1 - 2 * (i + 0.5) / 5000);\n const theta = Math.PI * (1 + Math.sqrt(5)) * i;\n positions[i*3] = Math.sin(phi) * Math.cos(theta) * 2;\n positions[i*3+1] = Math.sin(phi) * Math.sin(theta) * 2;\n positions[i*3+2] = Math.cos(phi) * 2;\n}\n\n// Vertex shader snippet\nvec3 displaced = position + normalize(position) * snoise(position * 2.0 + uTime * 0.5) * 0.3;\ndisplaced += normalize(position) * uExplode * 5.0;\ngl_PointSize = 3.0 * (300.0 / length(mvPosition.xyz));", "technologies": [ "React Three Fiber", "Three.js", "WebGL" ], "difficulty": "advanced", "tags": [ "3d", "particles", "sphere", "shader", "r3f", "interactive", "explode" ], "seen_in": "Creative developer portfolios, crypto/Web3 sites", "seen_in_url": "" }, { "id": "3d-card-tilt", "name_en": "3D Card Tilt", "name_he": "כרטיס נוטה תלת-מימדי", "category": "3d-effects", "description": "כרטיס שנוטה לכיוון העכבר בתלת-מימד עם שכבות parallax פנימיות. התמונה זזה בכיוון הפוך מהנטייה ליצירת עומק. צללים ושיקוף אור משתנים בזמן אמת.", "prompt": "Implement a 3D tilt hover effect on product cards using vanilla JavaScript and CSS transforms.\n1. Card container: perspective: 1000px on the parent.\n2. On mousemove over card: calculate tilt angles:\n - const rect = card.getBoundingClientRect();\n - const x = (e.clientX - rect.left) / rect.width; // 0 to 1\n - const y = (e.clientY - rect.top) / rect.height; // 0 to 1\n - const tiltX = (0.5 - y) * 25; // max 12.5 degrees\n - const tiltY = (x - 0.5) * 25;\n - card.style.transform = `perspective(1000px) rotateX(${tiltX}deg) rotateY(${tiltY}deg) scale3d(1.03,1.03,1.03)`;\n3. Inner image parallax: move in the OPPOSITE direction of tilt:\n - img.style.transform = `translateX(${(x-0.5)*-20}px) translateY(${(y-0.5)*-20}px) scale(1.1)`;\n4. Light reflection: add a ::before pseudo-element with a radial gradient that follows the cursor position on the card:\n - background: radial-gradient(circle at ${x*100}% ${y*100}%, rgba(255,255,255,0.15), transparent 60%);\n5. Dynamic shadow: box-shadow shifts opposite to tilt direction for realistic depth.\n6. On mouseleave: transition all values back to 0 with transition: transform 0.5s ease.\n7. Card styling: border-radius 16px, overflow hidden, dark background.\n8. Works with CSS only for the card structure, JS only for the tilt calculation.", "code_hint": "card.addEventListener('mousemove', (e) => {\n const rect = card.getBoundingClientRect();\n const x = (e.clientX - rect.left) / rect.width;\n const y = (e.clientY - rect.top) / rect.height;\n const tiltX = (0.5 - y) * 25;\n const tiltY = (x - 0.5) * 25;\n card.style.transform = `perspective(1000px) rotateX(${tiltX}deg) rotateY(${tiltY}deg) scale3d(1.03,1.03,1.03)`;\n // Light reflection\n card.querySelector('.shine').style.background =\n `radial-gradient(circle at ${x*100}% ${y*100}%, rgba(255,255,255,0.15), transparent 60%)`;\n // Parallax image\n card.querySelector('img').style.transform =\n `translateX(${(x-0.5)*-20}px) translateY(${(y-0.5)*-20}px) scale(1.1)`;\n});", "technologies": [ "JavaScript", "CSS" ], "difficulty": "intermediate", "tags": [ "3d", "tilt", "card", "hover", "parallax", "perspective", "interactive" ], "seen_in": "VanillaTilt.js showcase, premium e-commerce cards", "seen_in_url": "https://micku7zu.github.io/vanilla-tilt.js/" }, { "id": "spline-embedded-scene", "name_en": "Spline Embedded Scene", "name_he": "סצנת Spline מוטמעת", "category": "3d-effects", "description": "סצנה תלת-מימדית אינטראקטיבית שנבנית ב-Spline (עורך 3D ויזואלי) ומוטמעת ישירות באתר. ללא קוד תלת-מימד — עובדים בעורך חזותי ומייצאים רכיב React.", "prompt": "Embed an interactive Spline 3D scene into a React website.\n1. Design your 3D scene in Spline (spline.design) — for example: a floating laptop with animated screen content, orbiting geometric shapes, and mouse-tracking camera.\n2. In Spline editor: click Export > Code > React. Copy the scene URL.\n3. Install: npm install @splinetool/react-spline\n4. In your React component:\n import Spline from '@splinetool/react-spline';\n \n5. Add event handlers for object interaction:\n function onSplineMouseDown(e) {\n if (e.target.name === 'Button') { /* handle click */ }\n }\n \n6. Styling: wrap in a container with aspect-ratio: 16/9, border-radius, overflow hidden.\n7. Loading state: add a skeleton placeholder while the Spline scene loads.\n8. Performance: set Spline export quality to 'balanced'. On mobile, consider showing a static screenshot fallback.\n9. Optional: for advanced control, use @splinetool/runtime directly to manipulate objects programmatically.\nTip: Spline's free tier allows 2 files with public links. The viewer component is ~200KB.", "code_hint": "import Spline from '@splinetool/react-spline';\n\nexport default function Hero3D() {\n const [loading, setLoading] = useState(true);\n return (\n
\n {loading &&
}\n setLoading(false)}\n onSplineMouseDown={(e) => {\n if (e.target.name === 'CTA') navigate('/signup');\n }}\n />\n
\n );\n}", "technologies": [ "Spline", "React" ], "difficulty": "beginner", "tags": [ "3d", "spline", "no-code", "embedded", "interactive", "react", "visual-editor" ], "seen_in": "Spline showcase gallery", "seen_in_url": "https://spline.design" }, { "id": "3d-globe-markers", "name_en": "3D Globe with Markers", "name_he": "גלובוס תלת-מימדי עם סמנים", "category": "3d-effects", "description": "גלובוס כדור-הארץ תלת-מימדי אינטראקטיבי עם סמנים במיקומים ספציפיים. מסתובב אוטומטית וניתן לגרור אותו. קווים מחברים בין נקודות. מושלם לדפי About us של חברות גלובליות.", "prompt": "Build an interactive 3D globe with location markers using Three.js (or globe.gl library).\nOption A — With globe.gl:\n1. npm install globe.gl\n2. const globe = Globe()(document.getElementById('globe-container'))\n .globeImageUrl('//unpkg.com/three-globe/example/img/earth-blue-marble.jpg')\n .backgroundImageUrl('//unpkg.com/three-globe/example/img/night-sky.png')\n .pointsData(locations)\n .pointLat('lat').pointLng('lng')\n .pointColor(() => '#7c3aed')\n .pointAltitude(0.06)\n .pointRadius(0.5)\n .arcsData(connections)\n .arcColor(() => ['#06b6d4', '#7c3aed'])\n .arcDashLength(0.4)\n .arcDashAnimateTime(1500);\n3. Auto-rotate: globe.controls().autoRotate = true; globe.controls().autoRotateSpeed = 0.5;\n\nOption B — With React Three Fiber:\n1. Create a SphereGeometry (radius 2, segments 64). Apply earth texture.\n2. For each marker: convert lat/lng to 3D position on sphere surface:\n x = R * cos(lat) * cos(lng); y = R * sin(lat); z = R * cos(lat) * sin(lng);\n3. Place small glowing dot meshes at each marker position.\n4. Draw arcs between locations using CubicBezierCurve3 with a midpoint elevated above the sphere surface.\n5. OrbitControls with autoRotate, limited zoom range.\n6. Add atmosphere glow: a slightly larger transparent sphere with Fresnel shader.\n\nData: array of { lat, lng, label, city } objects for 5-10 office locations.", "code_hint": "// globe.gl quick setup\nimport Globe from 'globe.gl';\n\nconst locations = [\n { lat: 40.7, lng: -74.0, city: 'New York' },\n { lat: 51.5, lng: -0.1, city: 'London' },\n { lat: 35.7, lng: 139.7, city: 'Tokyo' },\n { lat: 32.1, lng: 34.8, city: 'Tel Aviv' }\n];\nconst myGlobe = Globe()\n (document.getElementById('globe'))\n .globeImageUrl(earthTexture)\n .pointsData(locations)\n .pointLat('lat').pointLng('lng')\n .pointColor(() => '#7c3aed')\n .arcsData(arcs)\n .arcColor(() => '#06b6d4');", "technologies": [ "Three.js", "globe.gl" ], "difficulty": "intermediate", "tags": [ "3d", "globe", "map", "markers", "interactive", "data-visualization", "three.js" ], "seen_in": "GitHub Globe, Stripe global presence page", "seen_in_url": "https://github.com/home" }, { "id": "infinite-tunnel", "name_en": "Infinite Tunnel Fly-Through", "name_he": "טיסה במנהרה אינסופית", "category": "3d-effects", "description": "מנהרה תלת-מימדית אינסופית שטסים בתוכה — קירות עם חלקיקים או טבעות זוהרות שעוברים לידך ונעלמים. אפקט של מהירות ותנועה קדימה.", "prompt": "Create an infinite tunnel fly-through effect using Three.js.\n1. Generate 50 ring-shaped objects (TorusGeometry, radius 4, tube 0.05) positioned along the Z axis, evenly spaced every 3 units from z=0 to z=-150.\n2. Each ring: random rotation on X and Y, emissive material in alternating colors (#7c3aed, #06b6d4).\n3. Animation: move all rings toward the camera (z += speed * delta). When a ring's z > 5 (behind camera), reset it to z = -150 with a new random rotation and color. This creates infinite forward motion.\n4. Add 500 small point particles scattered inside the tunnel cylinder (random radius < 3.5, random Z). Move them toward camera similarly.\n5. Camera: fixed at z=0, looking down -Z. Subtle mouse-driven camera offset (x,y shift by mouse position * 0.5) for parallax steering feel.\n6. Add postprocessing: UnrealBloomPass (strength 0.8) for glowing rings.\n7. Background: black.\n8. Speed control: base speed 0.5, increases on scroll-down, decreases on scroll-up.\n9. Optional: add a CRT scanline overlay div on top for retro sci-fi feel.\n10. Fog: exponential fog from z=-20 to create depth fadeout.", "code_hint": "const rings = Array.from({length: 50}, (_, i) => {\n const ring = new THREE.Mesh(\n new THREE.TorusGeometry(4, 0.05, 8, 64),\n new THREE.MeshStandardMaterial({\n color: i % 2 ? '#7c3aed' : '#06b6d4',\n emissive: i % 2 ? '#7c3aed' : '#06b6d4',\n emissiveIntensity: 0.5\n })\n );\n ring.position.z = -i * 3;\n ring.rotation.x = Math.random() * Math.PI;\n scene.add(ring);\n return ring;\n});\n\nfunction animate() {\n rings.forEach(r => {\n r.position.z += speed;\n if (r.position.z > 5) r.position.z -= 150;\n });\n}", "technologies": [ "Three.js", "WebGL" ], "difficulty": "intermediate", "tags": [ "3d", "tunnel", "infinite", "animation", "fly-through", "three.js", "space" ], "seen_in": "Music visualizers, sci-fi themed landing pages", "seen_in_url": "" }, { "id": "3d-terrain-landscape", "name_en": "3D Terrain Landscape", "name_he": "נוף שטח תלת-מימדי", "category": "3d-effects", "description": "נוף הרים תלת-מימדי שנוצר פרוצדורלית מ-noise. PlaneGeometry עם vertex displacement יוצר טופוגרפיה ריאליסטית. צבעים משתנים לפי גובה.", "prompt": "Build a procedural 3D terrain landscape using Three.js.\n1. Create a PlaneGeometry(20, 20, 256, 256) — high-poly for smooth terrain.\n2. In a setup function, displace Y vertices using layered Perlin noise:\n for each vertex: y = noise2D(x*0.15, z*0.15) * 3.0 + noise2D(x*0.4, z*0.4) * 1.0 + noise2D(x*1.0, z*1.0) * 0.3\n This gives large hills + medium bumps + fine detail.\n3. Compute vertex normals after displacement: geometry.computeVertexNormals().\n4. Color by height: add vertex colors to the geometry:\n - Below -0.5: deep blue (water)\n - -0.5 to 0.5: green (lowland)\n - 0.5 to 1.5: brown (hills)\n - 1.5 to 2.5: gray (mountain)\n - Above 2.5: white (snow)\n5. Material: MeshStandardMaterial with vertexColors: true, flatShading: true for a low-poly aesthetic.\n6. Lighting: DirectionalLight simulating sun + soft AmbientLight. Add fog for atmosphere.\n7. Camera: OrbitControls, initial position looking down at 45 degrees.\n8. Animation: slowly shift the noise offset on Z to create a 'fly-over' effect, or animate the sun position for day/night cycle.\n9. Optional: add a water plane at y=-0.5 with MeshStandardMaterial (blue, opacity 0.7, transparent).", "code_hint": "const geometry = new THREE.PlaneGeometry(20, 20, 256, 256);\ngeometry.rotateX(-Math.PI / 2);\nconst positions = geometry.attributes.position;\nfor (let i = 0; i < positions.count; i++) {\n const x = positions.getX(i);\n const z = positions.getZ(i);\n const y = noise2D(x*0.15, z*0.15) * 3.0\n + noise2D(x*0.4, z*0.4) * 1.0\n + noise2D(x*1.0, z*1.0) * 0.3;\n positions.setY(i, y);\n}\ngeometry.computeVertexNormals();\n// Vertex colors by height\nconst colors = new Float32Array(positions.count * 3);\nfor (let i = 0; i < positions.count; i++) {\n const y = positions.getY(i);\n const color = y > 2.5 ? [1,1,1] : y > 1.5 ? [0.5,0.5,0.5] : y > 0.5 ? [0.4,0.3,0.2] : [0.2,0.6,0.3];\n colors.set(color, i*3);\n}", "technologies": [ "Three.js", "WebGL" ], "difficulty": "advanced", "tags": [ "3d", "terrain", "procedural", "noise", "landscape", "three.js", "generative" ], "seen_in": "Data visualization dashboards, game-like landing pages", "seen_in_url": "" }, { "id": "r3f-drei-stars-sparkles", "name_en": "React Three Fiber + Drei Stars/Sparkles", "name_he": "כוכבים וניצוצות עם Drei", "category": "3d-effects", "description": "רכיבי Stars ו-Sparkles מ-Drei — ספריית העזר של React Three Fiber. שורה אחת של קוד מוסיפה שמיים מלאי כוכבים או ניצוצות מנצנצים לסצנה תלת-מימדית.", "prompt": "Create a dreamy 3D background using Drei's Stars and Sparkles components in React Three Fiber.\n1. Install: npm install @react-three/fiber @react-three/drei\n2. Set up a full-viewport Canvas with a dark background:\n \n3. Add the Stars component:\n \n - radius: sphere radius for star placement\n - depth: camera depth range\n - count: number of stars\n - factor: size factor\n - fade: stars fade at edges\n - speed: twinkle/movement speed\n4. Add the Sparkles component:\n \n - count: number of sparkle particles\n - scale: spread area\n - size: sparkle size (randomized)\n - noise: movement noise factor\n5. Add subtle camera movement: useFrame to slowly rotate the camera around the Y axis at 0.1 speed.\n6. Optional: add Drei's wrapper around content to make it bob gently.\n7. Combine with page content overlaid on top using position: relative, z-index: 1.\n8. Performance: disable antialiasing on mobile, reduce star count to 2000.", "code_hint": "import { Canvas } from '@react-three/fiber';\nimport { Stars, Sparkles, Float } from '@react-three/drei';\n\nexport default function SpaceBackground() {\n return (\n \n \n \n \n \n {/* Optional: 3D content here */}\n \n \n );\n}", "technologies": [ "React Three Fiber", "Drei" ], "difficulty": "beginner", "tags": [ "3d", "stars", "sparkles", "drei", "r3f", "background", "space", "easy" ], "seen_in": "React Three Fiber documentation examples", "seen_in_url": "https://drei.docs.pmnd.rs/" }, { "id": "cloth-curtain-simulation", "name_en": "Cloth/Curtain Simulation", "name_he": "סימולציית בד/וילון", "category": "physics", "description": "סימולציית בד שמגיב לגרביטציה, רוח, וגרירת עכבר. רשת של חלקיקים מחוברים באילוצי מרחק (constraints) שיוצרים התנהגות של בד אמיתי. ניתן לקרוע אותו בלחיצה.", "prompt": "Build a cloth/curtain physics simulation using HTML5 Canvas.\n1. Create a particle grid (30 columns x 20 rows). Each particle has: x, y, oldX, oldY (for Verlet integration), pinned (boolean).\n2. Pin the top row of particles (they stay fixed — the curtain rod).\n3. Connect adjacent particles with constraints (horizontal + vertical + diagonal for stability). Constraint: maintain original rest distance between connected particles.\n4. Physics loop (run at 60fps):\n a. For each particle (not pinned): apply Verlet integration:\n vx = (x - oldX) * 0.99 (damping); vy = (y - oldY) * 0.99;\n oldX = x; oldY = y; x += vx; y += vy + gravity (0.5);\n b. Add wind force: x += sin(time * 2 + y * 0.05) * 0.3\n c. Satisfy constraints (iterate 5 times for stability):\n For each constraint: compute delta, move each particle halfway toward satisfying the rest length.\n d. If a constraint is stretched beyond 1.8x rest length, break it (cloth tearing).\n5. Mouse interaction: on drag, move the nearest particle to the cursor position.\n6. Rendering: draw constraint lines between connected particles. Use a gradient fill for the cloth surface.\n7. Add a 'Reset' button that rebuilds the grid.\n8. Add a 'Wind' toggle button.", "code_hint": "class Particle {\n constructor(x, y, pinned = false) {\n this.x = x; this.y = y;\n this.oldX = x; this.oldY = y;\n this.pinned = pinned;\n }\n update(gravity, wind) {\n if (this.pinned) return;\n const vx = (this.x - this.oldX) * 0.99;\n const vy = (this.y - this.oldY) * 0.99;\n this.oldX = this.x; this.oldY = this.y;\n this.x += vx + wind;\n this.y += vy + gravity;\n }\n}\nclass Constraint {\n constructor(p1, p2) {\n this.p1 = p1; this.p2 = p2;\n this.restLength = dist(p1, p2);\n }\n satisfy() {\n const dx = this.p2.x - this.p1.x;\n const dy = this.p2.y - this.p1.y;\n const d = Math.sqrt(dx*dx + dy*dy);\n const diff = (this.restLength - d) / d * 0.5;\n if (!this.p1.pinned) { this.p1.x -= dx*diff; this.p1.y -= dy*diff; }\n if (!this.p2.pinned) { this.p2.x += dx*diff; this.p2.y += dy*diff; }\n }\n}", "technologies": [ "Canvas", "JavaScript" ], "difficulty": "advanced", "tags": [ "physics", "cloth", "curtain", "simulation", "verlet", "particles", "interactive" ], "seen_in": "Physics simulation demos, creative agency sites", "seen_in_url": "" }, { "id": "rope-chain-physics", "name_en": "Rope/Chain Physics", "name_he": "פיזיקת חבל/שרשרת", "category": "physics", "description": "חבל או שרשרת שנע בפיזיקה ריאליסטית. סדרה של חלקיקים מחוברים עם constraints כך שהמרחק ביניהם קבוע. ניתן לגרור מנקודה אחת ולראות את הגמישות.", "prompt": "Create a rope/chain physics simulation on HTML5 Canvas.\n1. Create a chain of 25 particles (nodes) connected sequentially. First node is pinned to a fixed point (or follows the mouse).\n2. Each node: x, y, oldX, oldY (Verlet integration). Spacing: 15px between nodes.\n3. Physics update per frame:\n a. Verlet integration for each non-pinned node:\n vx = (x - oldX) * 0.98; vy = (y - oldY) * 0.98;\n oldX = x; oldY = y; x += vx; y += vy + 0.6 (gravity);\n b. Constraint solving (iterate 8 times for stiff rope):\n For each pair of connected nodes: compute current distance. If != restLength (15px), move each node equally to satisfy the constraint.\n4. Mouse interaction: the first node follows the mouse cursor directly (pinned to cursor position). The rest of the chain follows with realistic swinging.\n5. Rendering: draw smooth curves through the nodes using quadratic bezier curves (ctx.quadraticCurveTo) for each segment instead of straight lines.\n6. Visual style: stroke width 4px with a gradient from dark to light along the chain. Draw small circles at each node (3px radius).\n7. Add a second rope with different length and color to show two independent chains.\n8. On click: spawn a weight (heavy ball) attached to the end of the chain.\n9. Collision: keep all nodes within canvas bounds (bounce off edges).", "code_hint": "const nodes = Array.from({length: 25}, (_, i) => ({\n x: anchorX, y: anchorY + i * 15,\n oldX: anchorX, oldY: anchorY + i * 15\n}));\n\nfunction update() {\n // Pin first node to mouse\n nodes[0].x = mouseX; nodes[0].y = mouseY;\n // Verlet\n for (let i = 1; i < nodes.length; i++) {\n const n = nodes[i];\n const vx = (n.x - n.oldX) * 0.98;\n const vy = (n.y - n.oldY) * 0.98;\n n.oldX = n.x; n.oldY = n.y;\n n.x += vx; n.y += vy + 0.6;\n }\n // Constraints (8 iterations)\n for (let iter = 0; iter < 8; iter++)\n for (let i = 0; i < nodes.length - 1; i++) {\n const a = nodes[i], b = nodes[i+1];\n const dx = b.x-a.x, dy = b.y-a.y;\n const d = Math.sqrt(dx*dx+dy*dy);\n const diff = (15 - d) / d * 0.5;\n if (i > 0) { a.x -= dx*diff; a.y -= dy*diff; }\n b.x += dx*diff; b.y += dy*diff;\n }\n}", "technologies": [ "Canvas", "JavaScript" ], "difficulty": "intermediate", "tags": [ "physics", "rope", "chain", "verlet", "simulation", "interactive", "drag" ], "seen_in": "Physics playgrounds, creative dev experiments", "seen_in_url": "" }, { "id": "magnetic-repulsion-grid", "name_en": "Magnetic Repulsion Grid", "name_he": "רשת דחייה מגנטית", "category": "physics", "description": "רשת של אלמנטים שנדחפים הצידה כשהעכבר עובר ביניהם — כאילו יש כוח מגנטי דוחה. האלמנטים חוזרים למקום בתנועת קפיץ כשהעכבר מתרחק.", "prompt": "Create a magnetic repulsion grid where items push away from the cursor.\n1. Create a 10x10 grid of square tiles (40px each, 8px gap). Each tile has a home position (fixed) and a current position (animated).\n2. On mousemove: for each tile, calculate distance from cursor to tile center.\n - If distance < 120px (repulsion radius):\n force = (120 - distance) / 120;\n direction = normalize(tile.center - cursor);\n tile.offsetX = direction.x * force * 40;\n tile.offsetY = direction.y * force * 40;\n - If distance >= 120px: target offset is (0, 0).\n3. Spring physics for smooth return: each frame:\n tile.vx += (targetOffsetX - tile.offsetX) * 0.08;\n tile.vy += (targetOffsetY - tile.offsetY) * 0.08;\n tile.vx *= 0.85; tile.vy *= 0.85; (damping)\n tile.offsetX += tile.vx; tile.offsetY += tile.vy;\n4. Apply transform: translate(offsetX, offsetY) and scale(1 + force * 0.1) for a 'bulge' effect.\n5. Tiles closer to cursor also increase their brightness/opacity.\n6. CSS grid layout for the base positions. JS handles the offset transforms.\n7. Each tile: subtle gradient background, border-radius 8px, shadow that increases with displacement.\n8. On mobile: use touch position instead of mouse.", "code_hint": "const tiles = document.querySelectorAll('.grid-tile');\nconst state = Array.from(tiles, (t) => ({\n el: t, homeX: 0, homeY: 0, offsetX: 0, offsetY: 0, vx: 0, vy: 0\n}));\n\nfunction animate() {\n state.forEach(tile => {\n const rect = tile.el.getBoundingClientRect();\n const cx = rect.left + rect.width/2;\n const cy = rect.top + rect.height/2;\n const dx = cx - mouseX, dy = cy - mouseY;\n const dist = Math.sqrt(dx*dx + dy*dy);\n let targetX = 0, targetY = 0;\n if (dist < 120) {\n const force = (120 - dist) / 120;\n targetX = (dx/dist) * force * 40;\n targetY = (dy/dist) * force * 40;\n }\n tile.vx += (targetX - tile.offsetX) * 0.08;\n tile.vy += (targetY - tile.offsetY) * 0.08;\n tile.vx *= 0.85; tile.vy *= 0.85;\n tile.offsetX += tile.vx; tile.offsetY += tile.vy;\n tile.el.style.transform = `translate(${tile.offsetX}px,${tile.offsetY}px)`;\n });\n requestAnimationFrame(animate);\n}", "technologies": [ "JavaScript", "CSS" ], "difficulty": "intermediate", "tags": [ "physics", "magnetic", "repulsion", "grid", "hover", "spring", "interactive" ], "seen_in": "Creative agency grids, portfolio galleries", "seen_in_url": "" }, { "id": "sand-gravity-particles", "name_en": "Sand/Gravity Particles", "name_he": "חלקיקי חול/כבידה", "category": "physics", "description": "חלקיקי חול שנופלים ומצטברים כמו שעון חול. כל חלקיק מושפע מכבידה ומתנגש עם חלקיקים אחרים. יוצרים ערימות ריאליסטיות. ניתן לשפוך חול חדש בלחיצה.", "prompt": "Build a sand/gravity particle simulation on HTML5 Canvas.\n1. Use a grid-based cellular automaton approach (much faster than per-particle physics):\n - Create a 2D grid (200 columns x 150 rows). Each cell is empty (0) or sand (1-5 for colors).\n - Cell size: 4px x 4px for a pixelated look.\n2. Falling rules (process bottom-to-top, random left-right order):\n a. If cell below is empty: move down.\n b. If cell below is occupied: try below-left or below-right (random order). If one is empty, move diagonally.\n c. If all three below are occupied: sand rests (stops moving).\n3. On mouse drag: spawn sand particles in a 5px radius around the cursor. Color: random from sandy palette ['#f4d03f','#d4a017','#c0892e','#a0724a','#deb887'].\n4. Render: draw each occupied cell as a small filled rect in its color.\n5. Performance: use Uint8Array for the grid. Process only cells that might move (skip settled cells). Target 60fps.\n6. Add a few fixed 'walls' — solid cells that can't move (draw in dark gray). Players can click to place walls.\n7. Add a 'hourglass' button that clears the bottom half and flips gravity briefly.\n8. Clear button: empties the grid.\n9. On mobile: touch and drag to pour sand.", "code_hint": "const cols = 200, rows = 150, cellSize = 4;\nconst grid = new Uint8Array(cols * rows); // 0=empty, 1-5=sand colors\n\nfunction update() {\n for (let y = rows - 2; y >= 0; y--) {\n // Random L-R order prevents bias\n const dir = Math.random() > 0.5 ? 1 : -1;\n for (let xi = 0; xi < cols; xi++) {\n const x = dir > 0 ? xi : cols - 1 - xi;\n const i = y * cols + x;\n if (grid[i] === 0) continue;\n const below = (y+1) * cols + x;\n if (grid[below] === 0) {\n grid[below] = grid[i]; grid[i] = 0;\n } else {\n const bl = (y+1)*cols + (x-1);\n const br = (y+1)*cols + (x+1);\n if (x>0 && grid[bl]===0) { grid[bl]=grid[i]; grid[i]=0; }\n else if (x\n2. Apply to:\n - Modal: initial={{ scale: 0.85, opacity: 0 }} animate={{ scale: 1, opacity: 1 }}\n - Card hover: whileHover={{ y: -8, scale: 1.02 }} transition={{ type: 'spring', stiffness: 400, damping: 17 }}\n - Drag: \n - List items: stagger with custom spring per item\n3. Explain the 3 spring parameters:\n - stiffness: higher = snappier (100=gentle, 500=snappy)\n - damping: higher = less bounce (5=very bouncy, 30=no bounce)\n - mass: higher = heavier/slower\n\nShow 4 UI examples side by side with different spring configs so the user can see the difference.", "code_hint": "// Vanilla spring\nclass Spring {\n constructor(target, stiffness=180, damping=12) {\n this.position = 0; this.velocity = 0;\n this.target = target;\n this.stiffness = stiffness; this.damping = damping;\n }\n update(dt = 1/60) {\n const force = -this.stiffness * (this.position - this.target);\n const damp = -this.damping * this.velocity;\n this.velocity += (force + damp) * dt;\n this.position += this.velocity * dt;\n return this.position;\n }\n isDone() {\n return Math.abs(this.velocity) < 0.01 && Math.abs(this.position - this.target) < 0.1;\n }\n}\n\n// Framer Motion:\n", "technologies": [ "JavaScript" ], "difficulty": "intermediate", "tags": [ "physics", "spring", "animation", "framer-motion", "ui", "bounce", "interactive" ], "seen_in": "Every Framer Motion site, Apple UI animations", "seen_in_url": "https://www.framer.com/motion/" }, { "id": "procedural-ambient-soundscape", "name_en": "Procedural Ambient Soundscape", "name_he": "נוף קולי פרוצדורלי", "category": "audio-effects", "description": "צלילים אמביינטיים שנוצרים פרוצדורלית ב-Web Audio API בלי קבצי סאונד. שכבות של oscillators, noise, ופילטרים יוצרות אווירת חלל, יער, או ים שמשתנה כל הזמן.", "prompt": "Build a procedural ambient soundscape engine using the Web Audio API — no audio files needed.\n1. Create an AudioContext (unlock on first user click/tap).\n2. Layer 1 — Drone: create 3 OscillatorNodes (type: 'sine') at frequencies 55Hz, 82.5Hz, 110Hz (A octaves). Connect each through a GainNode (volume 0.05-0.1). Slowly modulate each frequency with an LFO: another OscillatorNode at 0.1Hz connected to the main oscillator's frequency param with gain 2-5Hz.\n3. Layer 2 — Shimmer: create a high OscillatorNode (800-1200Hz, type: 'triangle'). Connect through a BiquadFilterNode (type: 'bandpass', Q: 10) and GainNode (0.02). Modulate the filter frequency slowly with another LFO (0.05Hz).\n4. Layer 3 — Texture: generate white noise using AudioBufferSourceNode with a buffer filled with Math.random()*2-1. Connect through a BiquadFilterNode (type: 'lowpass', frequency: 400Hz) and GainNode (0.03).\n5. Layer 4 — Random events: every 8-15 seconds, trigger a short 'ping' (sine wave at random frequency 400-2000Hz, envelope: attack 0.1s, decay 2s, connected through ConvolverNode for reverb).\n6. Master output: connect all through a master GainNode with a volume slider (0-1).\n7. Create a simple UI: Play/Pause button, volume slider, toggles for each layer.\n8. On page visibility hidden: suspend AudioContext. On visible: resume.", "code_hint": "const ctx = new AudioContext();\n\n// Layer 1: Drone\nfunction createDrone(freq) {\n const osc = ctx.createOscillator();\n osc.type = 'sine'; osc.frequency.value = freq;\n const lfo = ctx.createOscillator();\n lfo.frequency.value = 0.1;\n const lfoGain = ctx.createGain();\n lfoGain.gain.value = 3;\n lfo.connect(lfoGain).connect(osc.frequency);\n const gain = ctx.createGain();\n gain.gain.value = 0.07;\n osc.connect(gain).connect(master);\n osc.start(); lfo.start();\n}\n\n// Layer 3: Filtered noise\nconst buffer = ctx.createBuffer(1, ctx.sampleRate * 2, ctx.sampleRate);\nconst data = buffer.getChannelData(0);\nfor (let i = 0; i < data.length; i++) data[i] = Math.random()*2-1;\nconst noise = ctx.createBufferSource();\nnoise.buffer = buffer; noise.loop = true;\nconst filter = ctx.createBiquadFilter();\nfilter.type = 'lowpass'; filter.frequency.value = 400;\nnoise.connect(filter).connect(noiseGain).connect(master);\nnoise.start();", "technologies": [ "JavaScript" ], "difficulty": "advanced", "tags": [ "audio", "soundscape", "procedural", "ambient", "web-audio-api", "generative", "no-samples" ], "seen_in": "ARES-X Space Audio system", "seen_in_url": "" }, { "id": "mic-reactive-blob", "name_en": "Mic-Reactive Blob", "name_he": "בועה מגיבה למיקרופון", "category": "audio-effects", "description": "צורה אורגנית שמגיבה לקלט מיקרופון בזמן אמת. Web Audio API מנתח את עוצמת הקול ומשנה את גודל ועיוות הצורה בהתאם — ככל שהקול חזק יותר, הבועה מתעוותת יותר.", "prompt": "Create a microphone-reactive blob visualization using Web Audio API and Canvas (or Three.js).\n1. Request microphone access: navigator.mediaDevices.getUserMedia({ audio: true }).\n2. Create AudioContext, connect stream to an AnalyserNode:\n - analyser.fftSize = 256;\n - const dataArray = new Uint8Array(analyser.frequencyBinCount);\n3. On each frame: analyser.getByteFrequencyData(dataArray);\n - Calculate average volume: sum(dataArray) / dataArray.length / 255 (normalized 0-1).\n - Calculate bass energy: average of first 8 frequency bins.\n - Calculate treble energy: average of last 32 frequency bins.\n4. Draw a blob on Canvas:\n - Base radius: 100px.\n - For each of 64 points around the circle:\n angle = (i / 64) * TWO_PI;\n radiusOffset = volume * 50 + bass * 30 * sin(angle * 3 + time) + treble * 20 * sin(angle * 7 + time * 2);\n x = centerX + (baseRadius + radiusOffset) * cos(angle);\n y = centerY + (baseRadius + radiusOffset) * sin(angle);\n - Connect points with smooth bezier curves.\n5. Fill with a radial gradient that changes hue based on volume.\n6. Add a glow effect: draw the blob twice — once large and blurred (shadowBlur: 30), once sharp on top.\n7. Show a 'Click to enable microphone' button initially.\n8. Fallback: if microphone denied, use an audio file input instead.\n9. Display dB meter on the side.", "code_hint": "navigator.mediaDevices.getUserMedia({ audio: true })\n .then(stream => {\n const ctx = new AudioContext();\n const source = ctx.createMediaStreamSource(stream);\n const analyser = ctx.createAnalyser();\n analyser.fftSize = 256;\n source.connect(analyser);\n const data = new Uint8Array(analyser.frequencyBinCount);\n \n function draw() {\n analyser.getByteFrequencyData(data);\n const vol = data.reduce((a,b) => a+b) / data.length / 255;\n const bass = data.slice(0,8).reduce((a,b)=>a+b) / 8 / 255;\n // Draw blob with radius = 100 + vol * 50\n canvasCtx.beginPath();\n for (let i = 0; i <= 64; i++) {\n const angle = (i/64) * Math.PI * 2;\n const r = 100 + vol*50 + bass*30*Math.sin(angle*3+time);\n const x = cx + r * Math.cos(angle);\n const y = cy + r * Math.sin(angle);\n i === 0 ? canvasCtx.moveTo(x,y) : canvasCtx.lineTo(x,y);\n }\n canvasCtx.fill();\n requestAnimationFrame(draw);\n }\n draw();\n });", "technologies": [ "JavaScript", "Canvas" ], "difficulty": "advanced", "tags": [ "audio", "microphone", "reactive", "blob", "visualization", "web-audio-api", "interactive" ], "seen_in": "Voice assistant UIs, music apps", "seen_in_url": "" }, { "id": "audio-visualizer-sphere", "name_en": "Audio Visualizer Sphere", "name_he": "כדור ויזואליזציית אודיו", "category": "audio-effects", "description": "כדור תלת-מימדי שמשתנה לפי מוזיקה. כל קודקוד נדחף החוצה לפי תדר ספציפי מנתוני הספקטרום. בתדרים נמוכים הכדור פועם, בגבוהים הוא מתעוות.", "prompt": "Build a 3D audio visualizer sphere using Three.js and Web Audio API.\n1. Audio setup:\n - Create AudioContext from an