• 01 - Critter Models 02
  • 02 - VantaJS Shaders
  • 03 - App Bundle 02
  • 04 - Cathedrals
  • 05 - App Bundle 01
  • 06 - Retroscopic
  • 07 - Games
  • 08 - Voronoi
  • 09 - Terrain
  • 10 - Critter Models 01
  • 11 - Chromatophores
  • 12 - Conformal Maps
  • 13 - Echo Logix
  • 14 - Music
  • 15 - Colour Theory
MIKE'S NOTEBOOK
Notes
Number:
Project:
01Critter Models 02
└ Locust└ Butterfly└ Cockroach└ Spider└ Scorpion└ Dragon└ Brazilian Treehopper└ Tardigrade└ Diatoms└ Bumblebee└ Wasp└ Hammerhead Worm└ Giraffe Weevil└ Trilobite Beetle
02VantaJS Shaders
└ Clouds└ Waves└ Cells└ Rings└ Halo└ Globe└ Net└ Dots└ Birds└ Trunk└ Topology└ About VantaJS
03App Bundle 02
└ Gutindex└ Insect Builder└ OpenLayers
04Cathedrals
└ Lattice└ Lost└ Lucent└ Lumen└ Glass└ Disco└ Hyperion└ Latent
05App Bundle 01
└ Robohash└ PrismJS└ Horoscope└ Binary Jazz└ Morse Code└ URL Extractor└ Entity Escaper└ Phonetic Alphabet└ Toki Pona└ Semaphore└ Cut Up Machine└ Text Mixer└ Currency Freaks└ Host IO└ CrUX API└ Logo Dev└ Web Speech API└ GEO DB
06Retroscopic
└ Galaxy└ Ocean└ Slime└ 24-Cell└ Unicorn└ Polyp└ Oneiric 1└ Oneiric 2└ Node└ Torus Knot└ GLSL Grid└ Liquid Crystal└ Silky└ Butterfly└ Ocean 2└ Icosahedron└ Luma└ Swarm└ Broken Solid└ Hypersphere└ Constraints└ Psycho Killer
01 / 03

LOGO DEV - INTERACTIVE APP SOURCE CODE

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="referrer" content="origin">
    <title>Logo Search</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            background-color: #000;
            color: #fff;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
            min-height: 100vh;
            padding: 20px;
        }

        .container {
            max-width: 1200px;
            margin: 0 auto;
        }

        header {
            text-align: center;
            margin-bottom: 40px;
            padding: 20px 0;
            border-bottom: 1px solid #333;
        }

        h1 {
            font-size: 2.5rem;
            margin-bottom: 10px;
            background: linear-gradient(90deg, #fff, #aaa);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
        }

        .subtitle {
            color: #aaa;
            font-size: 1rem;
            text-align:left;
        }

        .search-container {
            background: #111;
            border-radius: 12px;
            padding: 30px;
            margin-bottom: 30px;
            border: 1px solid #222;
        }

        .search-box {
            display: flex;
            gap: 10px;
            margin-bottom: 20px;
        }

        input[type="text"] {
            flex: 1;
            padding: 15px 20px;
            border: 2px solid #333;
            border-radius: 8px;
            background: #000;
            color: #fff;
            font-size: 1rem;
            transition: border-color 0.3s ease;
        }

        input[type="text"]:focus {
            outline: none;
            border-color: #666;
        }

        button {
            padding: 15px 30px;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            border: none;
            border-radius: 8px;
            font-size: 1rem;
            font-weight: 600;
            cursor: pointer;
            transition: transform 0.2s ease, opacity 0.2s ease;
        }

        button:hover {
            transform: translateY(-2px);
            opacity: 0.9;
        }

        button:disabled {
            background: #333;
            cursor: not-allowed;
            transform: none;
            opacity: 0.5;
        }

        .examples {
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
            margin-top: 10px;
        }

        .example-tag {
            padding: 8px 15px;
            background: #222;
            border-radius: 20px;
            font-size: 0.9rem;
            color: #ccc;
            cursor: pointer;
            transition: background 0.2s ease;
        }

        .example-tag:hover {
            background: #333;
        }

        .loading {
            text-align: center;
            padding: 40px;
            color: #aaa;
        }

        .loading-spinner {
            width: 40px;
            height: 40px;
            border: 3px solid #333;
            border-top: 3px solid #667eea;
            border-radius: 50%;
            animation: spin 1s linear infinite;
            margin: 0 auto 20px;
        }

        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }

        .results-container {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
            gap: 25px;
            margin-top: 20px;
        }

        .logo-card {
            background: #111;
            border-radius: 12px;
            padding: 20px;
            text-align: center;
            border: 1px solid #222;
            transition: transform 0.3s ease, border-color 0.3s ease;
            display: flex;
            flex-direction: column;
            align-items: center;
            height: 100%;
        }

        .logo-card:hover {
            transform: translateY(-5px);
            border-color: #444;
        }

        .logo-container {
            width: 140px;
            height: 140px;
            margin: 0 auto 15px;
            display: flex;
            align-items: center;
            justify-content: center;
            background: #000;
            border-radius: 8px;
            border: 1px solid #333;
            overflow: hidden;
            flex: 0 0 auto;
        }

        .logo-img {
            width: 130px;
            height: 130px;
            object-fit: contain;
            display: block;
        }

        .company-name {
            font-size: 1.1rem;
            margin-bottom: 5px;
            color: #fff;
            line-height: 1.3;
            text-align: center;
            width: 100%;
        }

        .company-domain {
            color: #667eea;
            font-size: 0.9rem;
            margin-bottom: 15px;
            word-break: break-all;
            text-align: center;
            width: 100%;
        }

        .button-group {
            display: flex;
            gap: 5px;
            width: 100%;
            margin-top: auto;
        }

        .copy-button {
            padding: 8px 15px;
            background: #222;
            color: #ccc;
            border: none;
            border-radius: 6px;
            font-size: 0.85rem;
            cursor: pointer;
            transition: background 0.2s ease;
            flex: 1;
        }

        .copy-button:hover {
            background: #333;
        }

        .error-message {
            background: rgba(220, 38, 38, 0.1);
            border: 1px solid rgba(220, 38, 38, 0.3);
            color: #f87171;
            padding: 20px;
            border-radius: 8px;
            margin: 20px 0;
            text-align: center;
        }

        .no-results {
            text-align: center;
            color: #aaa;
            padding: 40px;
            grid-column: 1 / -1;
        }

        .success-message {
            background: rgba(34, 197, 94, 0.1);
            border: 1px solid rgba(34, 197, 94, 0.3);
            color: #4ade80;
            padding: 20px;
            border-radius: 8px;
            margin: 20px 0;
            text-align: center;
        }

        .image-loading {
            background: linear-gradient(90deg, #222 25%, #333 50%, #222 75%);
            background-size: 200% 100%;
            animation: loading 1.5s infinite;
        }

        @keyframes loading {
            0% { background-position: 200% 0; }
            100% { background-position: -200% 0; }
        }

        @media (max-width: 768px) {
            .results-container {
                grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
            }

            .search-box {
                flex-direction: column;
            }

            button {
                width: 100%;
            }

            h1 {
                font-size: 2rem;
            }

            .search-container {
                padding: 20px;
            }
        }

        @media (max-width: 480px) {
            body {
                padding: 10px;
            }

            .results-container {
                grid-template-columns: 1fr;
            }

            .logo-card {
                padding: 20px;
            }

            .logo-container {
                width: 130px;
                height: 130px;
            }

            .logo-img {
                width: 120px;
                height: 120px;
            }
        }

        .notification {
            position: fixed;
            top: 20px;
            right: 20px;
            padding: 12px 20px;
            border-radius: 8px;
            z-index: 1000;
            animation: slideIn 0.3s ease, fadeOut 0.3s ease 2s forwards;
            box-shadow: 0 4px 12px rgba(0,0,0,0.3);
        }

        .notification-success {
            background: #4CAF50;
            color: white;
        }

        .notification-error {
            background: #f44336;
            color: white;
        }

        @keyframes slideIn {
            from {
                transform: translateX(100%);
                opacity: 0;
            }
            to {
                transform: translateX(0);
                opacity: 1;
            }
        }

        @keyframes fadeOut {
            to {
                opacity: 0;
                transform: translateY(-20px);
            }
        }

        .logo-placeholder {
            color: #666;
            font-size: 0.8rem;
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <p class="subtitle">Find and download professional logos instantly</p>
        </header>

        <div class="search-container">
            <div class="search-box">
                <input type="text" id="searchInput" placeholder="Search for a company (e.g., Google, Nike, Apple)...">
                <button id="searchButton">Search</button>
            </div>

            <div class="examples">
                <div class="example-tag" data-company="Google">Google</div>
                <div class="example-tag" data-company="Apple">Apple</div>
                <div class="example-tag" data-company="Microsoft">Microsoft</div>
                <div class="example-tag" data-company="Amazon">Amazon</div>
                <div class="example-tag" data-company="Nike">Nike</div>
                <div class="example-tag" data-company="Stripe">Stripe</div>
                <div class="example-tag" data-company="Spotify">Spotify</div>
            </div>

            <div class="debug-info" style="background: #1a1a1a; border-radius: 8px; padding: 15px; margin-top: 20px; font-family: monospace; font-size: 0.9rem; color: #aaa;">
                <strong>Status:</strong> <span id="status">Ready</span><br>
                <strong>Note ID:</strong> 26b7853a-5395-4317-afd3-5aec15281fb1
            </div>
        </div>

        <div id="resultsArea"></div>
    </div>

    <script>
        // Configuration
        const API_BASE_URL = 'https://api.logo.dev';
        const YOUR_NOTE_ID = '26b7853a-5395-4317-afd3-5aec15281fb1';

        // DOM Elements - cache references
        const dom = {
            searchInput: document.getElementById('searchInput'),
            searchButton: document.getElementById('searchButton'),
            resultsArea: document.getElementById('resultsArea'),
            statusEl: document.getElementById('status')
        };

        // State
        let isLoading = false;
        let currentResults = [];

        // Initialize
        window.addEventListener('DOMContentLoaded', () => {
            setupEventListeners();
            updateStatus(typeof window.proxyFetch === 'function' ? 'Ready' : 'Error: proxyFetch not found');
        });

        // Setup all event listeners at once
        function setupEventListeners() {
            dom.searchButton.addEventListener('click', performSearch);
            dom.searchInput.addEventListener('keypress', e => e.key === 'Enter' && performSearch());

            // Delegate example tag clicks
            document.querySelector('.examples').addEventListener('click', e => {
                const tag = e.target.closest('.example-tag');
                if (tag) {
                    dom.searchInput.value = tag.dataset.company;
                    performSearch();
                }
            });
        }

        // Update status helper
        function updateStatus(text, color = '#44ff44') {
            dom.statusEl.textContent = text;
            dom.statusEl.style.color = color;
        }

        // Main search function
        async function performSearch() {
            const query = dom.searchInput.value.trim();

            if (!query) {
                showError('Please enter a company name');
                return;
            }

            setLoading(true);
            dom.resultsArea.innerHTML = '<div class="loading"><div class="loading-spinner"></div><p>Searching for logos...</p></div>';

            try {
                updateStatus('Searching...');

                const response = await window.proxyFetch(
                    `${API_BASE_URL}/search?q=${encodeURIComponent(query)}`,
                    {
                        method: 'GET',
                        headers: { 'Authorization': 'Bearer {{LOGO_DEV_SECRET}}' }
                    },
                    YOUR_NOTE_ID
                );

                if (response.status === 401) {
                    throw new Error('Authentication failed (401)');
                }

                if (!response.ok) {
                    throw new Error(`API returned ${response.status}`);
                }

                const data = await response.json();
                currentResults = data;
                displayResults(data);
                updateStatus(`Found ${data.length} results`);

            } catch (error) {
                console.error('Search error:', error);
                showError(`Failed to fetch logos: ${error.message}`);
                updateStatus('Error', '#ff4444');
            } finally {
                setLoading(false);
            }
        }

        // Display search results - simplified and optimized
        function displayResults(results) {
            if (!results || results.length === 0) {
                dom.resultsArea.innerHTML = '<div class="no-results"><h3>No logos found</h3><p>Try searching for a different company name</p></div>';
                return;
            }

            // Build HTML more efficiently using array join
            const cards = results.map((result, index) => {
                const { domain = '', name = 'Unknown', logo_url = '' } = result;
                const id = `logo-${index}`;

                return `
                    <div class="logo-card">
                        <div id="${id}" class="logo-container image-loading">
                            <img class="logo-img"
                                 alt="${name} logo"
                                 data-url="${logo_url}"
                                 data-id="${id}">
                        </div>
                        <h3 class="company-name">${name}</h3>
                        <p class="company-domain">${domain}</p>
                        <div class="button-group">
                            <button class="copy-button" data-action="copy" data-url="${logo_url}">Copy URL</button>
                        </div>
                    </div>
                `;
            }).join('');

            dom.resultsArea.innerHTML = `
                <div class="success-message">
                    <h3>✓ Found ${results.length} logos</h3>
                    <p>Loading logos...</p>
                </div>
                <div class="results-container">${cards}</div>
            `;

            // Load images after DOM is ready
            requestAnimationFrame(() => loadAllLogos());

            // Setup button listeners using event delegation
            setupButtonListeners();
        }

        // Load all logos efficiently
        function loadAllLogos() {
            const images = document.querySelectorAll('.logo-img');

            images.forEach(img => {
                const url = img.dataset.url;
                const id = img.dataset.id;
                const container = document.getElementById(id);

                img.onload = () => container.classList.remove('image-loading');
                img.onerror = () => handleImageError(img, container, url);
                img.src = url;
            });
        }

        // Handle image load errors
        async function handleImageError(img, container, url) {
            try {
                // Try loading via proxy
                const response = await window.proxyFetch(url, { method: 'GET' }, YOUR_NOTE_ID);

                if (response.ok) {
                    const blob = await response.blob();
                    img.src = URL.createObjectURL(blob);
                } else {
                    throw new Error('Proxy fetch failed');
                }
            } catch (error) {
                container.innerHTML = '<div class="logo-placeholder">Logo not available</div>';
                container.classList.remove('image-loading');
            }
        }

        // Setup button listeners using event delegation
        function setupButtonListeners() {
            const resultsContainer = document.querySelector('.results-container');
            if (!resultsContainer) return;

            resultsContainer.addEventListener('click', e => {
                const btn = e.target.closest('.copy-button');
                if (!btn) return;

                const action = btn.dataset.action;
                const url = btn.dataset.url;

                if (action === 'copy') {
                    copyToClipboard(url);
                }
            });
        }

        // Optimized clipboard copy
        async function copyToClipboard(text) {
            try {
                await navigator.clipboard.writeText(text);
                showNotification('Logo URL copied to clipboard!');
            } catch (err) {
                // Fallback for older browsers
                const textarea = document.createElement('textarea');
                textarea.value = text;
                textarea.style.cssText = 'position:fixed;left:-9999px;opacity:0';
                document.body.appendChild(textarea);
                textarea.select();

                try {
                    document.execCommand('copy');
                    showNotification('Logo URL copied to clipboard!');
                } catch (e) {
                    showNotification('Failed to copy', 'error');
                }

                document.body.removeChild(textarea);
            }
        }

        // Show error message
        function showError(message) {
            dom.resultsArea.innerHTML = `<div class="error-message"><h3>Error</h3><p>${message}</p></div>`;
        }

        // Set loading state
        function setLoading(loading) {
            isLoading = loading;
            dom.searchButton.disabled = loading;
            dom.searchButton.textContent = loading ? 'Searching...' : 'Search';
        }

        // Show notification - simplified
        function showNotification(message, type = 'success') {
            const notification = document.createElement('div');
            notification.className = `notification notification-${type}`;
            notification.textContent = message;
            document.body.appendChild(notification);

            setTimeout(() => notification.remove(), 2300);
        }
    </script>
</body>
</html>
MikeHiggins.me.uk LinkedIn GitHub Bluesky Contra Upwork
© 2026 Mike Higgins. All rights reserved.