1<!DOCTYPE html><html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"><meta name="format-detection" content="telephone=no"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"><meta name="apple-mobile-web-app-title" content="Simone Andreotti - Web Developer Portfolio"><meta name="theme-color" content="#000000"><meta name="robots" content="index, follow"><meta name="author" content="Simone Andreotti"><meta name="description" content="Simone Andreotti - Frontend developer - Personal portfolio and curricum to show off my life, experiences and works"><meta name="keywords" content="simone, simone andreotti, web development, portfolio, curriculum, web design, front-end developer, threejs, website development, 3d, vr, vr website, webxr"><meta name="language" content="English"><meta name="generator" content="Astro v5.16.0"><title>Simone Andreotti - Web Developer Portfolio</title><link rel="canonical" href="https://lab.simone-dev.com"><meta property="og:type" content="website"><meta property="og:url" content="https://lab.simone-dev.com"><meta property="og:title" content="Simone Andreotti - Web Developer Portfolio"><meta property="og:description" content="Explore my portfolio!"><meta property="og:image" content="https://lab.simone-dev.com/socialImage.png"><meta property="og:image:width" content="1200"><meta property="og:image:height" content="630"><meta property="og:locale" content="en_US"><meta property="og:site_name" content="Simone Andreotti Portfolio"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:url" content="https://lab.simone-dev.com"><meta name="twitter:title" content="Simone Andreotti - Web Developer Portfolio"><meta name="twitter:description" content="Explore my portfolio!"><meta name="twitter:image" content="https://lab.simone-dev.com/socialImage.png"><link rel="alternate" hreflang="en" href="https://lab.simone-dev.com"><link rel="alternate" hreflang="x-default" href="https://lab.simone-dev.com"><link rel="icon" type="image/png" href="/favicon/favicon-96x96.png" sizes="96x96"><link rel="icon" type="image/svg+xml" href="/favicon/favicon.svg"><link rel="shortcut icon" href="/favicon/favicon.ico"><link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png"><link rel="manifest" href="/manifest.json">
1<script>(function installSceneExportBridge() { 2 const EXPORT_PARAM = 'scene-export'; 3 4 function largestCanvas() { 5 return [...document.querySelectorAll('canvas')] 6 .filter((canvas) => canvas.width > 0 && canvas.height > 0) 7 .sort((a, b) => (b.width * b.height) - (a.width * a.height))[0] || null; 8 } 9 10 const searchParams = new URLSearchParams(window.location.search); 11 if (searchParams.get(EXPORT_PARAM) !== '1' || window.__sceneExport) return; 12 const headless = searchParams.get('scene-headless') === '1'; 13 document.documentElement.toggleAttribute('data-scene-headless', headless); 14 15 let virtualTime = 0; 16 let nextHandle = 1; 17 let callbacks = new Map(); 18 const nativeRequestAnimationFrame = window.requestAnimationFrame?.bind(window); 19 const nativeCancelAnimationFrame = window.cancelAnimationFrame?.bind(window); 20 const nativeNow = window.performance?.now?.bind(window.performance) || Date.now; 21 22 window.requestAnimationFrame = (callback) => { 23 const handle = nextHandle++; 24 callbacks.set(handle, callback); 25 return handle; 26 }; 27 window.cancelAnimationFrame = (handle) => callbacks.delete(handle); 28 try { 29 if (!window.performance) Object.defineProperty(window, 'performance', { configurable: true, value: {} }); 30 Object.defineProperty(window.performance, 'now', { configurable: true, value: () => virtualTime }); 31 } catch { 32 // Exact callback timestamps still cover scenes that use the RAF argument directly. 33 } 34 try { 35 Object.defineProperty(window, 'devicePixelRatio', { configurable: true, value: 1 }); 36 } catch { 37 // The iframe viewport still requests the exact output size when DPR cannot be overridden. 38 } 39 40 const api = { 41 version: 1, 42 getCanvas: largestCanvas, 43 hasAudio() { return typeof window.__sceneAudioExport?.renderChunk === 'function'; }, 44 renderAudioChunk(options) { 45 return window.__sceneAudioExport?.renderChunk?.(options) ?? null; 46 }, 47 get time() { 48 return virtualTime; 49 }, 50 async step(timeMs) { 51 virtualTime = Math.max(0, Number(timeMs) || 0); 52 const frameCallbacks = callbacks; 53 callbacks = new Map(); 54 let firstError = null; 55 for (const callback of frameCallbacks.values()) { 56 try { 57 callback(virtualTime); 58 } catch (error) { 59 firstError ||= error; 60 } 61 } 62 await Promise.resolve(); 63 if (firstError) throw firstError; 64 const canvas = largestCanvas(); 65 return canvas ? { width: canvas.width, height: canvas.height, callbacks: frameCallbacks.size } : null; 66 }, 67 setRecording(active, settings = {}) { 68 document.documentElement.toggleAttribute('data-scene-export', Boolean(active)); 69 document.documentElement.toggleAttribute('data-scene-headless', headless); 70 window.dispatchEvent(new CustomEvent('scene-export-mode', { 71 detail: { ...settings, active: Boolean(active), headless }, 72 })); 73 }, 74 restore() { 75 if (nativeRequestAnimationFrame) window.requestAnimationFrame = nativeRequestAnimationFrame; 76 if (nativeCancelAnimationFrame) window.cancelAnimationFrame = nativeCancelAnimationFrame; 77 try { 78 Object.defineProperty(window.performance, 'now', { configurable: true, value: nativeNow }); 79 } catch { 80 // The page is normally discarded with its iframe after export. 81 } 82 }, 83 }; 84 85 window.__sceneExport = api; 86 window.parent?.postMessage({ type: 'scene-export-ready', version: api.version }, window.location.origin); 87}()); 88</script>
88<script type="application/ld+json">{"@context":"https://schema.org","@type":"Person","name":"Simone Andreotti","jobTitle":"Web Developer","description":"I am a web developer specialized in creating innovative websites with complex ui.","url":"https://lab.simone-dev.com","image":"https://lab.simone-dev.com/socialImage.png","sameAs":["https://www.linkedin.com/in/simone-andreotti/"],"worksFor":{"@type":"Person","name":"Simone Andreotti","url":"https://lab.simone-dev.com"},"makesOffer":{"@type":"Offer","itemOffered":{"@type":"Service","name":"Website Development","description":"Professional web development services to build unique, modern, responsive websites tailored to your needs.","provider":{"@type":"Person","name":"Simone Andreotti"}},"priceSpecification":{"@type":"PriceSpecification","priceCurrency":"EUR","price":"Custom"},"availability":"https://schema.org/InStock","url":"https://lab.simone-dev.com?section=contact"}}</script>
88<script type="application/ld+json">{"@context":"https://schema.org","@type":"LocalBusiness","@id":"https://lab.simone-dev.com/#localbusiness","name":"Simone Andreotti","description":"Simone Andreotti - Frontend developer - Personal portfolio and curricum to show off my life, experiences and works","url":"https://lab.simone-dev.com","email":"[email protected]","priceRange":"$$","image":"https://lab.simone-dev.com/socialImage.png","openingHoursSpecification":[{"@type":"OpeningHoursSpecification","dayOfWeek":["Monday","Tuesday","Wednesday","Thursday","Friday"],"opens":"09:00","closes":"17:00"}],"sameAs":["https://www.linkedin.com/in/simone-andreotti/"]}</script>
88<script type="application/ld+json">{"@context":"https://schema.org","@type":"Organization","@id":"https://lab.simone-dev.com/#organization","name":"Simone Andreotti","url":"https://lab.simone-dev.com","logo":{"@type":"ImageObject","url":"https://lab.simone-dev.com/socialImage.png"},"contactPoint":{"@type":"ContactPoint","contactType":"customer service","email":"[email protected]","availableLanguage":["English","Italian"]},"sameAs":["https://www.linkedin.com/in/simone-andreotti/"]}</script>
88<script type="application/ld+json">{"@context":"https://schema.org","@type":"WebSite","@id":"https://lab.simone-dev.com/#website","url":"https://lab.simone-dev.com","name":"Simone Andreotti - Web Developer Portfolio","description":"Simone Andreotti - Frontend developer - Personal portfolio and curricum to show off my life, experiences and works","inLanguage":"en-US"}</script>
88<script> 89 const devHosts = new Set(['localhost', '127.0.0.1']); 90 const isDevHost = devHosts.has(window.location.hostname); 91 92 function forceDocumentNavigation(event) { 93 if (event.defaultPrevented) return; 94 if (event.button !== 0) return; 95 if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return; 96 97 const anchor = event.target instanceof Element 98 ? event.target.closest('a[href]') 99 : null; 100 if (!(anchor instanceof HTMLAnchorElement)) return; 101 if (anchor.target && anchor.target.toLowerCase() !== '_self') return; 102 if (anchor.hasAttribute('download')) return; 103 104 const href = anchor.getAttribute('href'); 105 if (!href || href.startsWith('#')) return; 106 107 const url = new URL(anchor.href, window.location.href); 108 if (url.origin !== window.location.origin) return; 109 110 event.preventDefault(); 111 window.location.assign(url.href); 112 } 113 114 function reloadOnHistoryNavigation(event) { 115 const navEntry = performance.getEntriesByType('navigation')[0]; 116 const isBackForward = navEntry && navEntry.type === 'back_forward'; 117 118 if (event.persisted || isBackForward) { 119 window.location.reload(); 120 } 121 } 122 123 async function clearDevServiceWorkerState() { 124 if (!('serviceWorker' in navigator)) return; 125 126 const registrations = await navigator.serviceWorker.getRegistrations(); 127 if (registrations.length === 0) return; 128 129 await Promise.all(registrations.map((registration) => registration.unregister())); 130 131 if ('caches' in window) { 132 const keys = await caches.keys(); 133 await Promise.all(keys.map((key) => caches.delete(key))); 134 } 135 136 const reloadMarker = '__dev_sw_cache_cleared__'; 137 if (!sessionStorage.getItem(reloadMarker)) { 138 sessionStorage.setItem(reloadMarker, '1'); 139 window.location.reload(); 140 } 141 } 142 143 if ('serviceWorker' in navigator) { 144 window.addEventListener('load', async () => { 145 if (isDevHost) { 146 await clearDevServiceWorkerState(); 147 return; 148 } 149 150 try { 151 const swExists = await fetch('/sw.js', { method: 'HEAD', cache: 'no-store' }) 152 .then((response) => response.ok) 153 .catch(() => false); 154 155 if (!swExists) return; 156 157 const registration = await navigator.serviceWorker.register('/sw.js'); 158 console.log('Service Worker registered:', registration.scope); 159 160 setInterval(() => { 161 registration.update(); 162 }, 60000); 163 164 registration.addEventListener('updatefound', () => { 165 const newWorker = registration.installing; 166 newWorker.addEventListener('statechange', () => { 167 if (newWorker.state === 'installed' && navigator.serviceWorker.controller) { 168 console.log('New Service Worker available, updating'); 169 newWorker.postMessage({ type: 'SKIP_WAITING' }); 170 } 171 }); 172 }); 173 } catch (error) { 174 console.warn('Service Worker registration failed:', error); 175 } 176 }); 177 } 178 179 document.addEventListener('click', forceDocumentNavigation, true); 180 window.addEventListener('pageshow', reloadOnHistoryNavigation); 181 182 if (navigator.storage && navigator.storage.persist) { 183 navigator.storage.persist().then(persistent => { 184 if (persistent) { 185 console.log('Persistent storage granted'); 186 } 187 }); 188 } 189 </script>
189<link rel="stylesheet" href="/_astro/blackhole-slider.BSodS520.css"></head> <body> <section class=""> <div id="cards-container" style="width: 100%; height: 100vh;"></div> </section>
189<script type="module" src="/_astro/cards.astro_astro_type_script_index_0_lang.dcsrVkU_.js"></script>
189
189<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v31edd6df95cf4e85bb4c19e7a9bdbcba1788362987495" integrity="sha512-iIg7k2xntmwu6/uSb5tpc/hySgZc4eoL31yB29W6tJFo2akwjPWcEqnCEdJvGexCL0KEQwVYv5BlowfhVz26hg==" data-cf-beacon='{"version":"2024.11.0","token":"6ee11fde4f1d47dabe3003f46747e56a","r":1,"spa":2}' crossorigin="anonymous"></script>
189 190</body></html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.