<html>
    <head>
        <title>Ben Barry</title>

        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta charset="utf-8">

        <meta name="description" content="Ben Barry is a visual artist and designer concerned with making the future of humanity the best it can be&mdash;for everyone.">
        <meta name="author" content="Ben Barry">

        <meta property="og:title" content="Ben Barry">
        <meta property="og:type" content="website">
        <meta property="og:url" content="https://benbarry.com/">
        <meta property="og:site_name" content="Ben Barry">
        <meta property="og:description" content="Ben Barry is a visual artist and designer concerned with making the future of humanity the best it can be&mdash;for everyone.">
        <meta property="og:image" content="https://benbarry.com/assets/images/benbarry-open-graph-image.jpg">

        <link rel="stylesheet" href="/assets/css/video-player.css?v=1">
        <script type="module" src="/assets/js/index-player.js?v=4"></script>
        <link rel="icon" href="/assets/icons/favicon.png" type="image/png">
        <link rel="icon" href="/assets/icons/favicon-light-mode.svg" type="image/svg+xml" media="(prefers-color-scheme: light)">
        <link rel="icon" href="/assets/icons/favicon-dark-mode.svg" type="image/svg+xml" media="(prefers-color-scheme: dark)">

        <link rel="preconnect" href="https://img.benb.co">
        <link rel="preconnect" href="https://vid.benb.co" crossorigin>
        <link rel="preconnect" href="https://fonts.googleapis.com">
        <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
        <link rel="preload" href="/assets/type/tiempos-text-web-regular.woff2" as="font" type="font/woff2" crossorigin>
                <link
            rel="preload"
            as="image"
            href="https://img.benb.co/w/2000/v2.2/media/benbarry-facebook-analog-research-lab-v1.jpg"
            imagesrcset="https://img.benb.co/w/480/v2.2/media/benbarry-facebook-analog-research-lab-v1.jpg 480w, https://img.benb.co/w/768/v2.2/media/benbarry-facebook-analog-research-lab-v1.jpg 768w, https://img.benb.co/w/1200/v2.2/media/benbarry-facebook-analog-research-lab-v1.jpg 1200w, https://img.benb.co/w/1600/v2.2/media/benbarry-facebook-analog-research-lab-v1.jpg 1600w, https://img.benb.co/w/2000/v2.2/media/benbarry-facebook-analog-research-lab-v1.jpg 2000w"
            imagesizes="calc(100vw - 4rem)"
            media="(min-width: 701px)"
            fetchpriority="high"
        >
        <link
            rel="preload"
            as="image"
            href="https://img.benb.co/w/1200/v2.2/media/benbarry-facebook-analog-research-lab-v1-mobile.jpg"
            imagesrcset="https://img.benb.co/w/480/v2.2/media/benbarry-facebook-analog-research-lab-v1-mobile.jpg 480w, https://img.benb.co/w/768/v2.2/media/benbarry-facebook-analog-research-lab-v1-mobile.jpg 768w, https://img.benb.co/w/1200/v2.2/media/benbarry-facebook-analog-research-lab-v1-mobile.jpg 1200w"
            imagesizes="calc(100vw - 2rem)"
            media="(max-width: 700px)"
            fetchpriority="high"
        >
        <link rel="preload" as="image" href="https://img.benb.co/w/2000/v2.2/media/benbarry-openai-blossom-black.svg" imagesrcset="https://img.benb.co/w/480/v2.2/media/benbarry-openai-blossom-black.svg 480w, https://img.benb.co/w/768/v2.2/media/benbarry-openai-blossom-black.svg 768w, https://img.benb.co/w/1200/v2.2/media/benbarry-openai-blossom-black.svg 1200w, https://img.benb.co/w/1600/v2.2/media/benbarry-openai-blossom-black.svg 1600w, https://img.benb.co/w/2000/v2.2/media/benbarry-openai-blossom-black.svg 2000w" imagesizes="(max-width: 700px) calc(100vw - 2rem), calc(100vw - 4rem)" media="(prefers-color-scheme: light)" fetchpriority="high">
        <link rel="preload" as="image" href="https://img.benb.co/w/2000/v2.2/media/benbarry-openai-blossom-white.svg" imagesrcset="https://img.benb.co/w/480/v2.2/media/benbarry-openai-blossom-white.svg 480w, https://img.benb.co/w/768/v2.2/media/benbarry-openai-blossom-white.svg 768w, https://img.benb.co/w/1200/v2.2/media/benbarry-openai-blossom-white.svg 1200w, https://img.benb.co/w/1600/v2.2/media/benbarry-openai-blossom-white.svg 1600w, https://img.benb.co/w/2000/v2.2/media/benbarry-openai-blossom-white.svg 2000w" imagesizes="(max-width: 700px) calc(100vw - 2rem), calc(100vw - 4rem)" media="(prefers-color-scheme: dark)" fetchpriority="high">
                <link href="https://fonts.googleapis.com/css2?family=Lato:wght@300;400;700&display=swap" rel="stylesheet">

        <link rel="stylesheet" href="/assets/css/style.css?v=2.2.12">
    <script nonce="C6jbUnaJTXue3rkeaQkibYwE" src="https://cdn.usefathom.com/script.js" data-site="NLHVEYKD" defer></script>
</head>
    <body>
        <nav>
    <ul class="nav-left">
        <li><a href="/" class="current">Featured</a></li>
        <li><a href="/information" >Information</a></li>
        <li><a href="/writing" >Writing</a></li>
    </ul>
    <ul class="nav-right">
        <!-- <li><a href="https://logo.vc" target="_blank">Logo.vc</a></li> -->
        <li><a href="https://nonlinear.co" target="_blank" rel="noopener noreferrer" title="Nonlinear"><span class="nav-label">Nonlinear</span></a></li>
        <li><a href="https://particular.co" target="_blank" rel="noopener noreferrer" title="Particular"><span class="nav-label">Particular</span></a></li>
        <li><a href="https://activematrix.io" target="_blank" rel="noopener noreferrer" title="Active Matrix"><span class="nav-label">Active Matrix</span></a></li>
    </ul>
</nav>        <div class="reveal-nav reveal-nav-left">
            <ul class="nav-left">
                <li><a href="/" class="active">Featured</a></li>
                <li><a href="/information">Information</a></li>
                <li><a href="/writing">Writing</a></li>
            </ul>
        </div>
        <div role="navigation" class="reveal-nav reveal-nav-center portfolio-view" aria-label="Portfolio view">
            <ul class="nav-center">
                <li><a href="/" class="active" aria-current="page">Scroll</a></li>
                <li><a href="/pan">Pan</a></li>
            </ul>
        </div>
        <div class="reveal-nav reveal-nav-right">
            <ul class="nav-right">
                <!-- <li><a href="https://logo.vc" target="_blank" alt="Logo.vc" title="Logo.vc"><span class="sr-only">Logo.vc</span></a></li> -->
                <li><a href="https://nonlinear.co" target="_blank" rel="noopener noreferrer" alt="Nonlinear" title="Nonlinear"><span class="sr-only">Nonlinear</span></a></li>
                <li><a href="https://particular.co" target="_blank" rel="noopener noreferrer" alt="Particular" title="Particular"><span class="sr-only">Particular</span></a></li>
                <li><a href="https://activematrix.io" target="_blank" rel="noopener noreferrer" alt="Active Matrix" title="Active Matrix"><span class="sr-only">Active Matrix</span></a></li>
            </ul>
        </div>
                <header>
            <a href="/information" class="block" data-title="Learn More">
                <div id="stage"></div>
                <h1>Ben Barry is a visual artist and designer<br>
                    concerned with making the future of humanity<br>
                    the best it can be&mdash;for&nbsp;everyone.</h1>
            </a>
        </header>
        <div class="section-break featured-work-heading">
            <h2 class="featured">Featured Work</h2>
            <div role="navigation" class="portfolio-view-inline" aria-label="Featured work view">
                <ul class="nav-center">
                    <li><a href="/" class="active" aria-current="page">Scroll</a></li>
                    <li><a href="/pan">Pan</a></li>
                </ul>
            </div>
        </div>
        <main></main>
        <div class="section-break" id="thanks-section" style="display: none;">
            <h2 class="peace">Thanks For Scrolling</h2>
        </div>
        <nav>
    <ul class="nav-left">
        <li><a href="/" class="current">Featured</a></li>
        <li><a href="/information" >Information</a></li>
        <li><a href="/writing" >Writing</a></li>
    </ul>
    <ul class="nav-right">
        <!-- <li><a href="https://logo.vc" target="_blank">Logo.vc</a></li> -->
        <li><a href="https://nonlinear.co" target="_blank" rel="noopener noreferrer" title="Nonlinear"><span class="nav-label">Nonlinear</span></a></li>
        <li><a href="https://particular.co" target="_blank" rel="noopener noreferrer" title="Particular"><span class="nav-label">Particular</span></a></li>
        <li><a href="https://activematrix.io" target="_blank" rel="noopener noreferrer" title="Active Matrix"><span class="nav-label">Active Matrix</span></a></li>
    </ul>
</nav>        <script src="/assets/js/header.js?v=2.2.4"></script>
        <script src="/assets/js/loader.js"></script>
        <script src="/assets/js/work-section.js?v=2.2.8"></script>
        <script nonce="C6jbUnaJTXue3rkeaQkibYwE">
            // Prevent browser from restoring scroll position on reload
            if ('scrollRestoration' in history) {
                history.scrollRestoration = 'manual';
            }
            // Scroll to top immediately
            window.scrollTo(0, 0);

            // Handle reveal-nav visibility based on scroll
            let lastScrollTop = 0;
            const revealNavs = document.querySelectorAll('.reveal-nav');
            const scrollThreshold = 100;
            const portfolioRevealNav = document.querySelector('.reveal-nav.portfolio-view');

            function updatePortfolioRevealVisibility(visible) {
                const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
                // Tie the exit to scroll distance so it finishes at 650px,
                // even when a fast scroll crosses the whole range in one frame.
                if (visible && scrollTop <= 850) {
                    const progress = Math.min(1, Math.max(0, (850 - scrollTop) / 200));
                    portfolioRevealNav.style.transition = 'none';
                    portfolioRevealNav.style.transform = `translateX(-50%) translateY(calc(-${progress * 100}% - ${progress * 1.1}rem))`;
                    portfolioRevealNav.classList.toggle('visible', progress < 1);
                } else {
                    portfolioRevealNav.style.removeProperty('transition');
                    portfolioRevealNav.style.removeProperty('transform');
                    portfolioRevealNav.classList.toggle('visible', visible && scrollTop > 850);
                }
            }

            // Function to round reveal-nav dimensions to whole pixels
            function roundRevealNavDimensions() {
                // Clear previous measurements so a menu hidden at a narrow
                // breakpoint can recover its natural size when the window widens.
                revealNavs.forEach(nav => {
                    nav.style.removeProperty('width');
                    nav.style.removeProperty('height');
                });
                const sizes = Array.from(revealNavs, nav => ({nav, rect: nav.getBoundingClientRect()}));
                sizes.forEach(({nav, rect}) => {
                    if (!rect.width || !rect.height) return;
                    nav.style.width = `${Math.round(rect.width)}px`;
                    nav.style.height = `${Math.round(rect.height)}px`;
                });
            }

            function setRevealNavVisibility(visible) {
                revealNavs.forEach(nav => {
                    if (nav !== portfolioRevealNav) nav.classList.toggle('visible', visible);
                });
                updatePortfolioRevealVisibility(visible);
                // Round dimensions when visibility changes
                if (visible) {
                    // Use requestAnimationFrame to ensure dimensions are computed
                    requestAnimationFrame(() => {
                        roundRevealNavDimensions();
                    });
                }
            }

            // Round dimensions on window resize
            let revealNavResizeTimeout;
            window.addEventListener('resize', () => {
                clearTimeout(revealNavResizeTimeout);
                revealNavResizeTimeout = setTimeout(() => {
                    roundRevealNavDimensions();
                    updatePortfolioRevealVisibility(document.querySelector('.reveal-nav-left').classList.contains('visible'));
                }, 100);
            });

            window.addEventListener('scroll', () => {
                const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
                
                // Always hide if near top
                if (scrollTop < scrollThreshold) {
                    setRevealNavVisibility(false);
                    return;
                }

                // Show/hide based on scroll direction
                if (scrollTop < lastScrollTop) {
                    // Scrolling up
                    setRevealNavVisibility(true);
                } else {
                    // Scrolling down
                    setRevealNavVisibility(false);
                }

                lastScrollTop = scrollTop;
            });
            
            // Round dimensions on initial load and when sections are added
            setTimeout(() => {
                roundRevealNavDimensions();
            }, 100);

            // Shuffle
            function shuffleArray(array) {
                const newArray = [...array];
                for (let i = newArray.length - 1; i > 0; i--) {
                    const j = Math.floor(Math.random() * (i + 1));
                    [newArray[i], newArray[j]] = [newArray[j], newArray[i]];
                }
                return newArray;
            }

            function getGroupValue(item) {
                const parsed = Number(item?.group);
                return Number.isFinite(parsed) ? parsed : 50;
            }

            function orderByGroup(array) {
                const grouped = new Map();

                array.forEach(item => {
                    const groupValue = getGroupValue(item);
                    if (!grouped.has(groupValue)) {
                        grouped.set(groupValue, []);
                    }
                    grouped.get(groupValue).push(item);
                });

                const ordered = [];
                const sortedGroupKeys = Array.from(grouped.keys()).sort((a, b) => a - b);

                sortedGroupKeys.forEach(groupKey => {
                    const groupItems = shuffleArray(grouped.get(groupKey));
                    ordered.push(...groupItems);
                });

                return ordered;
            }

            const INITIAL_LOADING_DEBUG_MODE = new URLSearchParams(window.location.search).has('initialLoadingDebug');

            // Load and render work sections
            function initializeHomepage() {
                // Scroll to top on page load
                window.scrollTo(0, 0);
                
                const BATCH_SIZE = 10; // Number of sections to load per batch
                let currentBatch = 0;
                let allWorkItems = [];
                let isLoading = false;
                const main = document.querySelector('main');
                
                // Project requested via /{slug} URL, injected server-side (null on the plain homepage)
                const REQUESTED_PROJECT = null;

                // Hardcoded first work item - load immediately for maximum speed
                const FIRST_WORK_ITEM = {
                    "title": "Facebook Analog Research Laboratory v1",
                    "slug": "facebook-analog-research-laboratory-v1",
                    "files": ["benbarry-facebook-analog-research-lab-v1.jpg"],
                    "files_mobile": ["benbarry-facebook-analog-research-lab-v1-mobile.jpg"],
                    "link": "https://v1.benbarry.com/project/facebook-analog-research-laboratory",
                    "group": 1,
                    "mobile_horizontal_scroll": true,
                    "caption": "Collaboration with Everett Katigbak — Photo by Jesse Chehak for Wired Magazine",
                    "isFirstItem": true
                };

                // If the requested project happens to be the hardcoded first item,
                // skip its normal feed slot so it only appears above the header
                const SKIP_FIRST_WORK_ITEM = REQUESTED_PROJECT && REQUESTED_PROJECT.slug === FIRST_WORK_ITEM.slug;

                // Render the requested project into its dedicated slot below the nav
                if (REQUESTED_PROJECT) {
                    (async function loadRequestedProject() {
                        const container = document.getElementById('featured-project');
                        try {
                            await preloadFirstImage(REQUESTED_PROJECT);
                            const section = await createWorkSection({ ...REQUESTED_PROJECT, isFirstItem: true });
                            container.appendChild(section);
                        } catch (error) {
                            console.error('Error loading requested project:', error);
                            container.remove();
                        }
                    })();
                }
                
                // Create a single shared tooltip
                const tooltip = document.createElement('div');
                tooltip.className = 'tooltip';
                tooltip.style.cssText = `
                    position: fixed;
                    z-index: 1000;
                    pointer-events: none;
                    opacity: 0;
                    visibility: hidden;
                `;
                document.body.appendChild(tooltip);

                // Create loading indicator
                const loadingIndicator = document.createElement('div');
                loadingIndicator.className = 'loading-indicator';
                loadingIndicator.style.display = INITIAL_LOADING_DEBUG_MODE ? 'block' : 'none';
                loadingIndicator.style.textAlign = 'center';
                loadingIndicator.style.padding = '0 0 100vh 0';
                const loadingImgDark = document.createElement('img');
                loadingImgDark.className = 'loading-image-dark';
                loadingImgDark.src = 'assets/images/loading-dark.svg';
                loadingImgDark.alt = 'Loading...';
                loadingImgDark.width = 27;
                loadingImgDark.height = 27;
                loadingIndicator.appendChild(loadingImgDark);
                const loadingImgLight = document.createElement('img');
                loadingImgLight.className = 'loading-image-light';
                loadingImgLight.src = 'assets/images/loading-light.svg';
                loadingImgLight.alt = 'Loading...';
                loadingImgLight.width = 27;
                loadingImgLight.height = 27;
                loadingIndicator.appendChild(loadingImgLight);
                main.appendChild(loadingIndicator);

                // Create a placeholder section for the first item immediately (synchronously)
                // This ensures it's always first in the DOM, even before it's populated
                const firstSectionPlaceholder = document.createElement('section');
                firstSectionPlaceholder.className = 'block';
                firstSectionPlaceholder.id = 'first-work-item';
                main.insertBefore(firstSectionPlaceholder, loadingIndicator);

                // Immediately start loading the first work item (before work.json is fetched)
                (async function loadFirstWorkItem() {
                    if (SKIP_FIRST_WORK_ITEM) {
                        firstSectionPlaceholder.remove();
                        return;
                    }
                    try {
                        // Create the first section content
                        const firstSection = await createWorkSection(FIRST_WORK_ITEM);
                        
                        // Replace the placeholder with the actual section, ensuring it stays first
                        firstSectionPlaceholder.replaceWith(firstSection);
                        
                        // Set ID on the actual section for reference
                        firstSection.id = 'first-work-item';
                        
                        // Double-check: if somehow it's not first, move it to first position
                        if (main.firstChild !== firstSection) {
                            main.insertBefore(firstSection, main.firstChild);
                        }
                        
                        requestAnimationFrame(() => {
                            roundRevealNavDimensions();
                        });
                    } catch (error) {
                        console.error('Error loading first work item:', error);
                        // Remove placeholder on error
                        firstSectionPlaceholder.remove();
                    }
                })();

                // Function to check if we need to load more
                function checkScroll() {
                    if (isLoading) return;
                    
                    const scrollPosition = window.innerHeight + window.scrollY;
                    const threshold = document.documentElement.scrollHeight - 2000;
                    
                    if (scrollPosition > threshold) {
                        loadNextBatch();
                    }
                }

                // Function to preload first image URL
                async function preloadFirstImage(work) {
                        const firstFile = selectWorkMediaSource(work).files?.[0];
                    
                    if (firstFile && getMediaType(firstFile) === 'image') {
                        try {
                            const url = getMediaUrl(firstFile);
                            // Create preload link
                            const link = document.createElement('link');
                            link.rel = 'preload';
                            link.as = 'image';
                            link.href = url;
                            link.imageSrcset = getImageSrcSet(firstFile);
                            link.imageSizes = getImageSizes(1);
                            link.fetchPriority = 'high';
                            document.head.appendChild(link);
                        } catch (error) {
                            // Silently fail - preload is just an optimization
                        }
                    }
                }

                // Function to load next batch of sections
                async function loadNextBatch() {
                    if (isLoading || currentBatch * BATCH_SIZE >= allWorkItems.length) return;
                    
                    isLoading = true;
                    loadingIndicator.style.display = 'block';
                    
                    const start = currentBatch * BATCH_SIZE;
                    const end = Math.min(start + BATCH_SIZE, allWorkItems.length);
                    const batchItems = allWorkItems.slice(start, end);
                    
                    try {
                        // Load all items in batch sequentially
                        // Insert before loading indicator (normal behavior)
                        // The first work item is already in place and will stay first
                        for (const work of batchItems) {
                            const section = await createWorkSection({ ...work, priorityMedia: !REQUESTED_PROJECT && work.slug === 'openai-blossom-symbol' });
                            main.insertBefore(section, loadingIndicator);
                        }
                        currentBatch++;
                        
                        // Show "Thanks for Scrolling" section when all items are loaded
                        if (currentBatch * BATCH_SIZE >= allWorkItems.length) {
                            document.getElementById('thanks-section').style.display = 'flex';
                        }
                        
                        // Use requestAnimationFrame to ensure DOM is fully updated
                        requestAnimationFrame(() => {
                            roundRevealNavDimensions();
                        });
                    } catch (error) {
                        console.error('Error loading batch:', error);
                    } finally {
                        isLoading = false;
                        loadingIndicator.style.display = 'none';
                    }
                }

                // Initial load of work.json
                fetch('/work.json')
                    .then(response => {
                        if (!response.ok) {
                            throw new Error(`HTTP error! status: ${response.status}`);
                        }
                        return response.json();
                    })
                    .then(workItems => {
                        // Filter out the first work item and the requested project (already rendered above)
                        const filteredWorkItems = workItems.filter(item =>
                            item.slug !== FIRST_WORK_ITEM.slug &&
                            (!REQUESTED_PROJECT || item.slug !== REQUESTED_PROJECT.slug)
                        );
                        
                        // First shuffle all items
                        const shuffledWork = shuffleArray(filteredWorkItems);
                        
                        // Then order by group (lower group numbers first)
                        allWorkItems = orderByGroup(shuffledWork);
                        
                        if (INITIAL_LOADING_DEBUG_MODE) {
                            loadingIndicator.style.display = 'block';
                            return;
                        }
                        
                        // Load first batch (skip first item since it's already loaded)
                        loadNextBatch();
                        
                        // Add scroll listener
                        window.addEventListener('scroll', checkScroll);
                    })
                    .catch(error => {
                        console.error('Error loading work.json:', error);
                        main.innerHTML = '<section><div class="block">Error loading work items. Please try refreshing the page.</div></section>';
                    });
            }

            if (document.readyState === 'loading') {
                document.addEventListener('DOMContentLoaded', initializeHomepage, { once: true });
            } else {
                initializeHomepage();
            }

        </script>
    </body>
</html>