Created
March 9, 2026 18:20
-
-
Save borgel/74a6faf8de723de813835b5ddd9f2128 to your computer and use it in GitHub Desktop.
Interactive explainer of silicon semiconductor process nodes (250nm to 2nm)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Silicon Process Nodes: An Interactive Explainer</title> | |
| <style> | |
| :root { | |
| --bg: #0a0e17; | |
| --bg2: #111827; | |
| --bg3: #1e293b; | |
| --text: #e2e8f0; | |
| --text-dim: #94a3b8; | |
| --accent: #3b82f6; | |
| --accent2: #8b5cf6; | |
| --tsmc: #e11d48; | |
| --samsung: #2563eb; | |
| --intel: #0ea5e9; | |
| --gf: #10b981; | |
| --smic: #f59e0b; | |
| --border: #334155; | |
| --card: #1e293b; | |
| } | |
| * { margin: 0; padding: 0; box-sizing: border-box; } | |
| html { scroll-behavior: smooth; } | |
| body { | |
| font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; | |
| background: var(--bg); | |
| color: var(--text); | |
| line-height: 1.7; | |
| overflow-x: hidden; | |
| } | |
| /* NAV */ | |
| nav { | |
| position: fixed; top: 0; left: 0; right: 0; z-index: 100; | |
| background: rgba(10, 14, 23, 0.85); | |
| backdrop-filter: blur(16px); | |
| border-bottom: 1px solid var(--border); | |
| padding: 0 2rem; | |
| } | |
| nav .nav-inner { | |
| max-width: 1200px; margin: 0 auto; | |
| display: flex; align-items: center; justify-content: space-between; | |
| height: 56px; | |
| } | |
| nav .logo { font-weight: 700; font-size: 1rem; color: var(--accent); letter-spacing: -0.5px; } | |
| nav .nav-links { display: flex; gap: 1.5rem; list-style: none; } | |
| nav .nav-links a { | |
| color: var(--text-dim); text-decoration: none; font-size: 0.82rem; | |
| transition: color 0.2s; | |
| } | |
| nav .nav-links a:hover { color: var(--text); } | |
| @media (max-width: 768px) { | |
| nav .nav-links { display: none; } | |
| } | |
| /* HERO */ | |
| .hero { | |
| min-height: 100vh; | |
| display: flex; flex-direction: column; | |
| align-items: center; justify-content: center; | |
| text-align: center; | |
| padding: 6rem 2rem 4rem; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .hero::before { | |
| content: ''; | |
| position: absolute; inset: 0; | |
| background: radial-gradient(ellipse 80% 60% at 50% 40%, rgba(59,130,246,0.08), transparent), | |
| radial-gradient(ellipse 60% 40% at 30% 70%, rgba(139,92,246,0.06), transparent); | |
| pointer-events: none; | |
| } | |
| .hero h1 { | |
| font-size: clamp(2.5rem, 6vw, 4.5rem); | |
| font-weight: 800; | |
| letter-spacing: -2px; | |
| line-height: 1.1; | |
| margin-bottom: 1.5rem; | |
| background: linear-gradient(135deg, #e2e8f0 0%, #94a3b8 50%, #3b82f6 100%); | |
| -webkit-background-clip: text; -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| } | |
| .hero .subtitle { | |
| font-size: clamp(1rem, 2vw, 1.25rem); | |
| color: var(--text-dim); | |
| max-width: 640px; | |
| margin-bottom: 2.5rem; | |
| } | |
| .hero .node-ticker { | |
| display: flex; gap: 0.5rem; flex-wrap: wrap; justify-content: center; | |
| margin-bottom: 3rem; | |
| } | |
| .hero .node-ticker span { | |
| background: var(--bg3); | |
| border: 1px solid var(--border); | |
| padding: 0.3rem 0.75rem; | |
| border-radius: 999px; | |
| font-size: 0.8rem; | |
| font-weight: 600; | |
| color: var(--text-dim); | |
| transition: all 0.3s; | |
| } | |
| .hero .node-ticker span.active { | |
| border-color: var(--tsmc); | |
| color: var(--tsmc); | |
| box-shadow: 0 0 12px rgba(225,29,72,0.2); | |
| } | |
| .scroll-hint { | |
| position: absolute; bottom: 2rem; | |
| animation: bounce 2s infinite; | |
| color: var(--text-dim); font-size: 0.8rem; | |
| } | |
| @keyframes bounce { | |
| 0%, 100% { transform: translateY(0); } | |
| 50% { transform: translateY(8px); } | |
| } | |
| /* SECTIONS */ | |
| section { | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| padding: 6rem 2rem; | |
| } | |
| .section-header { | |
| margin-bottom: 3rem; | |
| } | |
| .section-header h2 { | |
| font-size: clamp(1.8rem, 4vw, 2.5rem); | |
| font-weight: 700; | |
| letter-spacing: -1px; | |
| margin-bottom: 0.75rem; | |
| } | |
| .section-header p { | |
| color: var(--text-dim); | |
| max-width: 700px; | |
| font-size: 1rem; | |
| } | |
| .section-divider { | |
| width: 60px; height: 3px; | |
| background: linear-gradient(90deg, var(--accent), var(--accent2)); | |
| border-radius: 2px; | |
| margin-bottom: 1rem; | |
| } | |
| /* WHAT NM MEANS */ | |
| .nm-explainer { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 2rem; | |
| } | |
| @media (max-width: 768px) { .nm-explainer { grid-template-columns: 1fr; } } | |
| .nm-card { | |
| background: var(--card); | |
| border: 1px solid var(--border); | |
| border-radius: 12px; | |
| padding: 2rem; | |
| } | |
| .nm-card h3 { | |
| font-size: 1.1rem; margin-bottom: 1rem; | |
| color: var(--accent); | |
| } | |
| .nm-card p { color: var(--text-dim); font-size: 0.92rem; } | |
| .nm-visual { | |
| grid-column: 1 / -1; | |
| background: var(--card); | |
| border: 1px solid var(--border); | |
| border-radius: 12px; | |
| padding: 2rem; | |
| overflow-x: auto; | |
| } | |
| .nm-bars { | |
| display: flex; | |
| align-items: flex-end; | |
| gap: 1rem; | |
| height: 200px; | |
| padding-top: 1rem; | |
| } | |
| .nm-bar-group { | |
| display: flex; flex-direction: column; | |
| align-items: center; gap: 0.5rem; | |
| flex: 1; min-width: 60px; | |
| } | |
| .nm-bar-stack { | |
| display: flex; flex-direction: column; | |
| align-items: center; gap: 2px; | |
| width: 100%; | |
| } | |
| .nm-bar { | |
| width: 100%; border-radius: 4px 4px 0 0; | |
| transition: all 0.5s; | |
| position: relative; | |
| cursor: pointer; | |
| } | |
| .nm-bar.marketing { | |
| background: linear-gradient(180deg, rgba(59,130,246,0.6), rgba(59,130,246,0.3)); | |
| border: 1px solid rgba(59,130,246,0.4); | |
| } | |
| .nm-bar.actual { | |
| background: linear-gradient(180deg, rgba(225,29,72,0.6), rgba(225,29,72,0.3)); | |
| border: 1px solid rgba(225,29,72,0.4); | |
| border-radius: 4px; | |
| } | |
| .nm-bar-label { | |
| font-size: 0.72rem; color: var(--text-dim); | |
| text-align: center; font-weight: 600; | |
| } | |
| .nm-bar-value { | |
| font-size: 0.65rem; color: var(--text-dim); | |
| position: absolute; top: -18px; width: 100%; text-align: center; | |
| } | |
| .nm-legend { | |
| display: flex; gap: 2rem; margin-top: 1.5rem; | |
| font-size: 0.82rem; color: var(--text-dim); | |
| } | |
| .nm-legend span { | |
| display: flex; align-items: center; gap: 0.5rem; | |
| } | |
| .nm-legend .swatch { | |
| width: 14px; height: 14px; border-radius: 3px; | |
| } | |
| /* TRANSISTOR TYPES */ | |
| .transistor-types { | |
| display: grid; | |
| grid-template-columns: repeat(3, 1fr); | |
| gap: 1.5rem; | |
| } | |
| @media (max-width: 768px) { .transistor-types { grid-template-columns: 1fr; } } | |
| .transistor-card { | |
| background: var(--card); | |
| border: 1px solid var(--border); | |
| border-radius: 12px; | |
| padding: 2rem; | |
| cursor: pointer; | |
| transition: all 0.3s; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .transistor-card:hover, .transistor-card.active { | |
| border-color: var(--accent); | |
| transform: translateY(-2px); | |
| box-shadow: 0 8px 32px rgba(59,130,246,0.1); | |
| } | |
| .transistor-card .era-badge { | |
| display: inline-block; | |
| background: rgba(59,130,246,0.15); | |
| color: var(--accent); | |
| padding: 0.2rem 0.6rem; | |
| border-radius: 999px; | |
| font-size: 0.72rem; | |
| font-weight: 600; | |
| margin-bottom: 1rem; | |
| } | |
| .transistor-card h3 { font-size: 1.3rem; margin-bottom: 0.75rem; } | |
| .transistor-card .years { color: var(--text-dim); font-size: 0.85rem; margin-bottom: 1rem; } | |
| .transistor-svg-container { | |
| width: 100%; | |
| height: 180px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| margin-bottom: 1rem; | |
| } | |
| .transistor-svg-container svg { max-width: 100%; max-height: 100%; } | |
| .transistor-card .description { | |
| color: var(--text-dim); font-size: 0.88rem; | |
| } | |
| .transistor-card .gate-coverage { | |
| margin-top: 1rem; | |
| display: flex; align-items: center; gap: 0.5rem; | |
| font-size: 0.8rem; font-weight: 600; | |
| } | |
| .gate-coverage .coverage-bar { | |
| flex: 1; height: 6px; background: var(--bg); border-radius: 3px; overflow: hidden; | |
| } | |
| .gate-coverage .coverage-fill { | |
| height: 100%; border-radius: 3px; | |
| transition: width 0.6s ease; | |
| } | |
| /* TIMELINE */ | |
| .timeline-controls { | |
| display: flex; gap: 0.5rem; margin-bottom: 2rem; flex-wrap: wrap; | |
| } | |
| .timeline-controls button { | |
| background: var(--bg3); | |
| border: 1px solid var(--border); | |
| color: var(--text-dim); | |
| padding: 0.4rem 1rem; | |
| border-radius: 999px; | |
| font-size: 0.8rem; | |
| cursor: pointer; | |
| transition: all 0.2s; | |
| font-weight: 500; | |
| } | |
| .timeline-controls button.active { | |
| border-color: var(--accent); | |
| color: var(--accent); | |
| background: rgba(59,130,246,0.1); | |
| } | |
| .timeline-controls button:hover { border-color: var(--accent); } | |
| .timeline-container { | |
| position: relative; | |
| overflow-x: auto; | |
| padding-bottom: 2rem; | |
| scrollbar-width: thin; | |
| scrollbar-color: var(--border) transparent; | |
| } | |
| .timeline-container::-webkit-scrollbar { height: 6px; } | |
| .timeline-container::-webkit-scrollbar-track { background: transparent; } | |
| .timeline-container::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; } | |
| .timeline-track { | |
| position: relative; | |
| min-width: max-content; | |
| padding: 2rem 0 4rem; | |
| } | |
| .timeline-line { | |
| position: absolute; | |
| top: 50%; | |
| left: 0; right: 0; | |
| height: 2px; | |
| background: var(--border); | |
| } | |
| .timeline-nodes { | |
| display: flex; | |
| gap: 0; | |
| position: relative; | |
| } | |
| .timeline-node { | |
| display: flex; flex-direction: column; | |
| align-items: center; | |
| min-width: 120px; | |
| position: relative; | |
| cursor: pointer; | |
| padding: 0 0.5rem; | |
| } | |
| .timeline-node .dot { | |
| width: 14px; height: 14px; | |
| border-radius: 50%; | |
| border: 2px solid var(--border); | |
| background: var(--bg); | |
| z-index: 2; | |
| transition: all 0.3s; | |
| position: relative; | |
| } | |
| .timeline-node:hover .dot, .timeline-node.expanded .dot { | |
| border-color: var(--tsmc); | |
| background: var(--tsmc); | |
| box-shadow: 0 0 12px rgba(225,29,72,0.4); | |
| transform: scale(1.3); | |
| } | |
| .timeline-node.samsung-dot:hover .dot, .timeline-node.samsung-dot.expanded .dot { | |
| border-color: var(--samsung); background: var(--samsung); | |
| box-shadow: 0 0 12px rgba(37,99,235,0.4); | |
| } | |
| .timeline-node.intel-dot:hover .dot, .timeline-node.intel-dot.expanded .dot { | |
| border-color: var(--intel); background: var(--intel); | |
| box-shadow: 0 0 12px rgba(14,165,233,0.4); | |
| } | |
| .timeline-node .node-label { | |
| position: absolute; | |
| bottom: calc(50% + 16px); | |
| font-size: 0.78rem; | |
| font-weight: 700; | |
| white-space: nowrap; | |
| transition: color 0.2s; | |
| } | |
| .timeline-node .node-year { | |
| position: absolute; | |
| top: calc(50% + 16px); | |
| font-size: 0.72rem; | |
| color: var(--text-dim); | |
| white-space: nowrap; | |
| } | |
| .timeline-node .node-detail { | |
| position: absolute; | |
| top: calc(50% + 36px); | |
| background: var(--card); | |
| border: 1px solid var(--border); | |
| border-radius: 10px; | |
| padding: 1.25rem; | |
| width: 280px; | |
| opacity: 0; | |
| pointer-events: none; | |
| transition: all 0.3s; | |
| transform: translateY(8px); | |
| z-index: 10; | |
| left: 50%; | |
| margin-left: -140px; | |
| } | |
| .timeline-node.expanded .node-detail { | |
| opacity: 1; pointer-events: auto; transform: translateY(0); | |
| } | |
| .node-detail h4 { | |
| font-size: 0.95rem; margin-bottom: 0.5rem; | |
| } | |
| .node-detail .detail-row { | |
| display: flex; justify-content: space-between; | |
| font-size: 0.78rem; color: var(--text-dim); | |
| padding: 0.25rem 0; | |
| border-bottom: 1px solid rgba(51,65,85,0.5); | |
| } | |
| .node-detail .detail-row:last-child { border-bottom: none; } | |
| .node-detail .detail-row .label { font-weight: 500; } | |
| .node-detail .detail-row .value { color: var(--text); font-weight: 600; } | |
| .node-detail .chips { | |
| margin-top: 0.75rem; | |
| font-size: 0.75rem; | |
| color: var(--text-dim); | |
| line-height: 1.5; | |
| } | |
| .node-detail .chips strong { color: var(--accent); } | |
| .node-detail .type-badge { | |
| display: inline-block; | |
| font-size: 0.65rem; font-weight: 600; | |
| padding: 0.15rem 0.5rem; | |
| border-radius: 999px; | |
| margin-bottom: 0.5rem; | |
| } | |
| .type-badge.planar { background: rgba(245,158,11,0.15); color: #f59e0b; } | |
| .type-badge.finfet { background: rgba(59,130,246,0.15); color: #3b82f6; } | |
| .type-badge.gaa { background: rgba(139,92,246,0.15); color: #8b5cf6; } | |
| /* DENSITY CHART */ | |
| .chart-container { | |
| background: var(--card); | |
| border: 1px solid var(--border); | |
| border-radius: 12px; | |
| padding: 2rem; | |
| overflow-x: auto; | |
| } | |
| .chart-header { | |
| display: flex; justify-content: space-between; align-items: center; | |
| margin-bottom: 1.5rem; flex-wrap: wrap; gap: 1rem; | |
| } | |
| .chart-header h3 { font-size: 1.1rem; } | |
| .chart-legend { | |
| display: flex; gap: 1.25rem; font-size: 0.78rem; | |
| } | |
| .chart-legend .legend-item { | |
| display: flex; align-items: center; gap: 0.4rem; | |
| color: var(--text-dim); cursor: pointer; | |
| transition: opacity 0.2s; | |
| } | |
| .chart-legend .legend-item.dimmed { opacity: 0.3; } | |
| .legend-dot { | |
| width: 10px; height: 10px; border-radius: 50%; | |
| } | |
| .density-chart { | |
| position: relative; | |
| height: 400px; | |
| min-width: 700px; | |
| } | |
| .density-chart svg { width: 100%; height: 100%; } | |
| /* FOUNDRY COMPARE */ | |
| .foundry-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); | |
| gap: 1.25rem; | |
| } | |
| .foundry-card { | |
| background: var(--card); | |
| border: 1px solid var(--border); | |
| border-radius: 12px; | |
| padding: 1.5rem; | |
| transition: all 0.3s; | |
| } | |
| .foundry-card:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 8px 24px rgba(0,0,0,0.3); | |
| } | |
| .foundry-card .foundry-name { | |
| font-size: 1.1rem; font-weight: 700; margin-bottom: 0.25rem; | |
| } | |
| .foundry-card .foundry-hq { | |
| font-size: 0.78rem; color: var(--text-dim); margin-bottom: 1rem; | |
| } | |
| .foundry-card .stat { margin-bottom: 0.75rem; } | |
| .foundry-card .stat-label { | |
| font-size: 0.72rem; color: var(--text-dim); text-transform: uppercase; | |
| letter-spacing: 0.5px; font-weight: 600; | |
| } | |
| .foundry-card .stat-value { | |
| font-size: 1.4rem; font-weight: 700; | |
| } | |
| .foundry-card .status-pill { | |
| display: inline-block; | |
| font-size: 0.7rem; font-weight: 600; | |
| padding: 0.2rem 0.6rem; | |
| border-radius: 999px; | |
| margin-top: 0.5rem; | |
| } | |
| /* EUV SECTION */ | |
| .euv-content { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 2rem; | |
| } | |
| @media (max-width: 768px) { .euv-content { grid-template-columns: 1fr; } } | |
| .euv-card { | |
| background: var(--card); | |
| border: 1px solid var(--border); | |
| border-radius: 12px; | |
| padding: 2rem; | |
| } | |
| .euv-card h3 { | |
| font-size: 1.05rem; margin-bottom: 1rem; | |
| display: flex; align-items: center; gap: 0.5rem; | |
| } | |
| .euv-card h3 .wavelength { | |
| font-size: 0.72rem; | |
| background: rgba(139,92,246,0.15); | |
| color: var(--accent2); | |
| padding: 0.15rem 0.5rem; | |
| border-radius: 999px; | |
| } | |
| .euv-adopters { | |
| grid-column: 1 / -1; | |
| } | |
| .euv-timeline { | |
| display: flex; gap: 0; position: relative; | |
| padding: 1rem 0; | |
| } | |
| .euv-timeline::before { | |
| content: ''; | |
| position: absolute; | |
| top: 50%; left: 0; right: 0; | |
| height: 2px; | |
| background: var(--border); | |
| } | |
| .euv-step { | |
| flex: 1; | |
| display: flex; flex-direction: column; | |
| align-items: center; | |
| text-align: center; | |
| position: relative; | |
| padding: 0 0.5rem; | |
| } | |
| .euv-step .euv-dot { | |
| width: 12px; height: 12px; | |
| border-radius: 50%; | |
| z-index: 2; | |
| margin-bottom: 0.5rem; | |
| } | |
| .euv-step .euv-label { | |
| font-size: 0.78rem; font-weight: 600; | |
| } | |
| .euv-step .euv-year { | |
| font-size: 0.72rem; color: var(--text-dim); | |
| position: absolute; bottom: -1.5rem; | |
| } | |
| /* MOORES LAW */ | |
| .moores-content { | |
| display: grid; | |
| grid-template-columns: 1.5fr 1fr; | |
| gap: 2rem; | |
| align-items: start; | |
| } | |
| @media (max-width: 768px) { .moores-content { grid-template-columns: 1fr; } } | |
| .moores-chart { | |
| background: var(--card); | |
| border: 1px solid var(--border); | |
| border-radius: 12px; | |
| padding: 2rem; | |
| overflow-x: auto; | |
| } | |
| .moores-sidebar { | |
| display: flex; flex-direction: column; gap: 1.25rem; | |
| } | |
| .moores-fact { | |
| background: var(--card); | |
| border: 1px solid var(--border); | |
| border-radius: 12px; | |
| padding: 1.5rem; | |
| } | |
| .moores-fact h4 { | |
| font-size: 0.9rem; margin-bottom: 0.5rem; color: var(--accent); | |
| } | |
| .moores-fact p { font-size: 0.85rem; color: var(--text-dim); } | |
| /* GLOBAL MILESTONES */ | |
| .milestones-list { | |
| display: grid; gap: 0; | |
| position: relative; | |
| padding-left: 2rem; | |
| } | |
| .milestones-list::before { | |
| content: ''; | |
| position: absolute; | |
| left: 7px; top: 0; bottom: 0; | |
| width: 2px; | |
| background: linear-gradient(180deg, var(--accent), var(--accent2)); | |
| } | |
| .milestone { | |
| display: flex; gap: 1.5rem; | |
| padding: 1rem 0; | |
| position: relative; | |
| } | |
| .milestone::before { | |
| content: ''; | |
| position: absolute; | |
| left: -2rem; | |
| top: 1.35rem; | |
| width: 10px; height: 10px; | |
| border-radius: 50%; | |
| background: var(--accent); | |
| border: 2px solid var(--bg); | |
| z-index: 2; | |
| margin-left: 3px; | |
| } | |
| .milestone.highlight::before { | |
| background: var(--tsmc); | |
| width: 12px; height: 12px; | |
| box-shadow: 0 0 8px rgba(225,29,72,0.4); | |
| margin-left: 2px; | |
| } | |
| .milestone .m-year { | |
| font-weight: 700; font-size: 0.9rem; | |
| color: var(--accent); | |
| min-width: 40px; | |
| } | |
| .milestone.highlight .m-year { color: var(--tsmc); } | |
| .milestone .m-text { | |
| font-size: 0.88rem; color: var(--text-dim); | |
| } | |
| /* FOOTER */ | |
| footer { | |
| border-top: 1px solid var(--border); | |
| padding: 3rem 2rem; | |
| text-align: center; | |
| color: var(--text-dim); | |
| font-size: 0.8rem; | |
| } | |
| footer a { color: var(--accent); text-decoration: none; } | |
| footer a:hover { text-decoration: underline; } | |
| /* TOOLTIP */ | |
| .tooltip { | |
| position: fixed; | |
| background: var(--bg2); | |
| border: 1px solid var(--border); | |
| border-radius: 8px; | |
| padding: 0.75rem 1rem; | |
| font-size: 0.8rem; | |
| pointer-events: none; | |
| z-index: 200; | |
| opacity: 0; | |
| transition: opacity 0.15s; | |
| max-width: 260px; | |
| box-shadow: 0 8px 32px rgba(0,0,0,0.5); | |
| } | |
| .tooltip.visible { opacity: 1; } | |
| .tooltip .tt-title { font-weight: 700; margin-bottom: 0.25rem; } | |
| .tooltip .tt-body { color: var(--text-dim); } | |
| /* ANIMATIONS */ | |
| .fade-in { | |
| opacity: 0; transform: translateY(24px); | |
| transition: opacity 0.6s, transform 0.6s; | |
| } | |
| .fade-in.visible { | |
| opacity: 1; transform: translateY(0); | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <nav> | |
| <div class="nav-inner"> | |
| <div class="logo">Silicon Nodes</div> | |
| <ul class="nav-links"> | |
| <li><a href="#nm-reality">What "nm" Means</a></li> | |
| <li><a href="#transistors">Transistor Types</a></li> | |
| <li><a href="#timeline">Timeline</a></li> | |
| <li><a href="#density">Density</a></li> | |
| <li><a href="#foundries">Foundries</a></li> | |
| <li><a href="#euv">EUV</a></li> | |
| <li><a href="#moores">Moore's Law</a></li> | |
| </ul> | |
| </div> | |
| </nav> | |
| <!-- HERO --> | |
| <div class="hero"> | |
| <h1>The Shrinking Transistor</h1> | |
| <p class="subtitle"> | |
| An interactive journey through semiconductor process nodes — from 250nm to 2nm — | |
| exploring how the world's foundries push the limits of physics to build the chips in your pocket. | |
| </p> | |
| <div class="node-ticker" id="nodeTicker"></div> | |
| <div class="scroll-hint">Scroll to explore ↓</div> | |
| </div> | |
| <!-- WHAT NM MEANS --> | |
| <section id="nm-reality" class="fade-in"> | |
| <div class="section-header"> | |
| <div class="section-divider"></div> | |
| <h2>What Does "nm" Actually Mean?</h2> | |
| <p>The nanometer labels on process nodes stopped reflecting physical reality long ago. Here's the truth.</p> | |
| </div> | |
| <div class="nm-explainer"> | |
| <div class="nm-card"> | |
| <h3>The Historical Truth</h3> | |
| <p>Through the mid-1990s, the "nm" label roughly matched the transistor gate length. Intel's nodes from 10µm (1972) through 350nm (1995) corresponded to actual physical dimensions.</p> | |
| </div> | |
| <div class="nm-card"> | |
| <h3>Modern Marketing</h3> | |
| <p>Today's "3nm" and "2nm" labels have <strong>no relation to any physical feature</strong>. A "3nm" transistor has a gate length of ~16–18nm and a metal pitch of ~23nm. The name is a branding designation indicating generational improvement.</p> | |
| </div> | |
| <div class="nm-visual"> | |
| <h3 style="margin-bottom:0.5rem; font-size:0.95rem;">Marketing Name vs. Actual Gate Length</h3> | |
| <p style="font-size:0.8rem; color:var(--text-dim); margin-bottom:1rem;"> | |
| As you can see, the gap between the marketed "nm" number and the actual physical gate length has grown dramatically. | |
| </p> | |
| <div class="nm-bars" id="nmBars"></div> | |
| <div class="nm-legend"> | |
| <span><span class="swatch" style="background:rgba(59,130,246,0.5)"></span> Marketing Name (nm)</span> | |
| <span><span class="swatch" style="background:rgba(225,29,72,0.5)"></span> Actual Gate Length (nm)</span> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- TRANSISTOR TYPES --> | |
| <section id="transistors" class="fade-in"> | |
| <div class="section-header"> | |
| <div class="section-divider"></div> | |
| <h2>Transistor Architecture Evolution</h2> | |
| <p>Three generations of transistor design, each solving the limitations of the last.</p> | |
| </div> | |
| <div class="transistor-types" id="transistorTypes"></div> | |
| </section> | |
| <!-- TIMELINE --> | |
| <section id="timeline" class="fade-in"> | |
| <div class="section-header"> | |
| <div class="section-divider"></div> | |
| <h2>Process Node Timeline</h2> | |
| <p>Click any node to see details. Filter by foundry to compare strategies.</p> | |
| </div> | |
| <div class="timeline-controls" id="timelineControls"></div> | |
| <div class="timeline-container"> | |
| <div class="timeline-track"> | |
| <div class="timeline-line"></div> | |
| <div class="timeline-nodes" id="timelineNodes"></div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- DENSITY CHART --> | |
| <section id="density" class="fade-in"> | |
| <div class="section-header"> | |
| <div class="section-divider"></div> | |
| <h2>Transistor Density Over Time</h2> | |
| <p>How many millions of transistors can you pack into a square millimeter? The answer has grown exponentially.</p> | |
| </div> | |
| <div class="chart-container"> | |
| <div class="chart-header"> | |
| <h3>MTr/mm² by Node (Log Scale)</h3> | |
| <div class="chart-legend" id="chartLegend"></div> | |
| </div> | |
| <div class="density-chart" id="densityChart"></div> | |
| </div> | |
| </section> | |
| <!-- FOUNDRIES --> | |
| <section id="foundries" class="fade-in"> | |
| <div class="section-header"> | |
| <div class="section-divider"></div> | |
| <h2>The Foundries</h2> | |
| <p>Five companies shape the world's chip manufacturing landscape. Only three can make leading-edge chips.</p> | |
| </div> | |
| <div class="foundry-grid" id="foundryGrid"></div> | |
| </section> | |
| <!-- EUV --> | |
| <section id="euv" class="fade-in"> | |
| <div class="section-header"> | |
| <div class="section-divider"></div> | |
| <h2>EUV Lithography</h2> | |
| <p>The technology that saved Moore's Law — printing circuits with 13.5nm light.</p> | |
| </div> | |
| <div class="euv-content"> | |
| <div class="euv-card"> | |
| <h3>What is EUV? <span class="wavelength">13.5nm wavelength</span></h3> | |
| <p style="color:var(--text-dim); font-size:0.9rem;"> | |
| Extreme Ultraviolet lithography uses 13.5nm wavelength light, down from 193nm with DUV (ArF immersion). | |
| This enables printing smaller features in fewer patterning steps. Without EUV, manufacturers must use | |
| expensive DUV multi-patterning (3× or 4× per layer), increasing cost, defects, and cycle time. | |
| </p> | |
| </div> | |
| <div class="euv-card"> | |
| <h3>Why It Matters</h3> | |
| <p style="color:var(--text-dim); font-size:0.9rem;"> | |
| EUV makes 7nm and below economically viable. SMIC's lack of EUV access (due to export controls) limits them to ~7nm-class | |
| nodes using DUV workarounds at much higher cost and lower yield. ASML is the sole supplier of EUV tools — each | |
| machine costs ~$200M and weighs 180 tons. | |
| </p> | |
| </div> | |
| <div class="euv-card euv-adopters"> | |
| <h3>EUV Adoption Timeline</h3> | |
| <div class="euv-timeline" id="euvTimeline"></div> | |
| </div> | |
| <div class="euv-card" style="grid-column: 1 / -1;"> | |
| <h3>Next Generation: High-NA EUV</h3> | |
| <p style="color:var(--text-dim); font-size:0.9rem;"> | |
| High-NA EUV (0.55 numerical aperture, vs 0.33 for current EUV) is the next step, enabling 2nm and below. | |
| ASML delivered the first High-NA EUV tools to Intel in 2024. Each costs ~$380M. These machines are essential for | |
| continuing density scaling beyond current limits. | |
| </p> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- MOORE'S LAW --> | |
| <section id="moores" class="fade-in"> | |
| <div class="section-header"> | |
| <div class="section-divider"></div> | |
| <h2>Moore's Law: Still Alive?</h2> | |
| <p>Gordon Moore's 1965 observation was never a law of physics — it was an economic prediction that the industry fought to maintain.</p> | |
| </div> | |
| <div class="moores-content"> | |
| <div class="moores-chart"> | |
| <h3 style="margin-bottom:1rem; font-size:1rem;">TSMC Transistor Density (MTr/mm²)</h3> | |
| <div id="mooresChart" style="height:360px; min-width:400px;"></div> | |
| </div> | |
| <div class="moores-sidebar"> | |
| <div class="moores-fact"> | |
| <h4>The Original Observation</h4> | |
| <p>In 1965, Gordon Moore predicted that the number of transistors on a chip would double approximately every two years.</p> | |
| </div> | |
| <div class="moores-fact"> | |
| <h4>Current Reality</h4> | |
| <p>Density still increases, but the pace has slowed from 2× every ~2 years to roughly 2× every ~3 years at leading edge. Cost per transistor reduction has also stalled below 7nm.</p> | |
| </div> | |
| <div class="moores-fact"> | |
| <h4>Sustained By Innovation</h4> | |
| <p>Each time scaling hit a wall, new innovations extended it: strained silicon, high-k metal gates, FinFET, EUV lithography, GAA nanosheets, backside power delivery, and 3D stacking.</p> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- KEY MILESTONES --> | |
| <section id="milestones" class="fade-in"> | |
| <div class="section-header"> | |
| <div class="section-divider"></div> | |
| <h2>Key Milestones</h2> | |
| <p>The defining moments in semiconductor manufacturing history.</p> | |
| </div> | |
| <div class="milestones-list" id="milestonesList"></div> | |
| </section> | |
| <footer> | |
| <p>Data compiled from WikiChip, IEEE, TSMC, Intel, Samsung, and industry sources. | |
| Transistor density figures use industry-standard NAND2+SFF cell methodology where available.</p> | |
| <p style="margin-top:0.5rem;">Built as an educational resource. Not affiliated with any semiconductor company.</p> | |
| </footer> | |
| <div class="tooltip" id="tooltip"> | |
| <div class="tt-title"></div> | |
| <div class="tt-body"></div> | |
| </div> | |
| <script> | |
| // ==================== DATA ==================== | |
| const tsmcNodes = [ | |
| { name:"250nm", year:1998, density:0.4, type:"planar", litho:"DUV (KrF)", features:"First deep-submicron TSMC node, 200mm wafers", chips:"Altera FPGAs, various ASICs" }, | |
| { name:"180nm", year:1999, density:0.8, type:"planar", litho:"DUV (KrF)", features:"Copper interconnects introduced industry-wide", chips:"Broadcom networking, ARM SoCs" }, | |
| { name:"130nm", year:2001, density:1.5, type:"planar", litho:"DUV (KrF)", features:"Low-k dielectrics introduced", chips:"ATI Radeon 9700" }, | |
| { name:"90nm", year:2004, density:2.5, type:"planar", litho:"DUV (ArF)", features:"300mm wafers, strained silicon", chips:"Xbox 360 GPU, Qualcomm" }, | |
| { name:"65nm", year:2006, density:4.0, type:"planar", litho:"DUV (ArF immersion)", features:"8 copper interconnect layers", chips:"Nvidia GeForce 8, Snapdragon S1" }, | |
| { name:"40nm", year:2008, density:8.0, type:"planar", litho:"DUV (ArF immersion)", features:"Half-node, led foundry segment", chips:"GTX 400, Snapdragon S2/S3" }, | |
| { name:"28nm", year:2011, density:15.3, type:"planar", litho:"DUV (ArF immersion)", features:"HKMG, still in production for IoT/auto", chips:"Snapdragon 800, Apple A7, Tegra K1" }, | |
| { name:"20nm", year:2014, density:20.8, type:"planar", litho:"DUV (double pattern)", features:"Last planar, short-lived", chips:"Apple A8/A8X" }, | |
| { name:"16nm", year:2015, density:28.9, type:"finfet", litho:"DUV (double pattern)", features:"TSMC's first FinFET node", chips:"Apple A9/A10, GTX 1080, Snapdragon 820" }, | |
| { name:"10nm", year:2017, density:52.5, type:"finfet", litho:"DUV (multi-pattern)", features:"Short-lived, quick transition to 7nm", chips:"Apple A11 Bionic" }, | |
| { name:"7nm", year:2018, density:91.2, type:"finfet", litho:"DUV / DUV+EUV (N7+)", features:"Massive node; N7+ = first TSMC EUV", chips:"A12/A13, Zen 2, RDNA, Snapdragon 855" }, | |
| { name:"5nm", year:2020, density:171.3, type:"finfet", litho:"DUV + EUV (~14 layers)", features:"Full EUV, 1.84x density vs 7nm", chips:"A14/A15, M1/M2, Zen 4, H100" }, | |
| { name:"3nm", year:2022, density:197.0, type:"finfet", litho:"DUV + EUV", features:"Most advanced FinFET, multiple variants", chips:"A17 Pro, M3/M4, Zen 5, SD 8 Gen 3" }, | |
| { name:"2nm", year:2025, density:313.0, type:"gaa", litho:"EUV (extensive)", features:"First TSMC GAA/nanosheet node", chips:"Apple A19 (expected), next-gen AMD/Nvidia" }, | |
| ]; | |
| const samsungNodes = [ | |
| { name:"14nm", year:2015, density:33.3, type:"finfet", features:"Samsung's first FinFET", chips:"Exynos 7420, Snapdragon 820" }, | |
| { name:"10nm", year:2017, density:51.8, type:"finfet", features:"1.6x density vs 14nm", chips:"Exynos 8895, Snapdragon 835" }, | |
| { name:"8nm", year:2018, density:61.2, type:"finfet", features:"Extension of 10nm, last DUV-only", chips:"Exynos 9820, RTX 3090/3080" }, | |
| { name:"7nm", year:2019, density:95.1, type:"finfet", features:"First Samsung EUV production node", chips:"Exynos 990, Snapdragon 765G" }, | |
| { name:"5nm", year:2020, density:127.0, type:"finfet", features:"25% density over 7nm", chips:"Exynos 2100, Snapdragon 888" }, | |
| { name:"4nm", year:2022, density:137.0, type:"finfet", features:"Last Samsung FinFET", chips:"Exynos 2200, Snapdragon 8 Gen 1" }, | |
| { name:"3nm GAA", year:2022, density:170.0, type:"gaa", features:"WORLD'S FIRST GAA production (MBCFET)", chips:"Limited adoption, yield challenges" }, | |
| ]; | |
| const intelNodes = [ | |
| { name:"22nm", year:2012, density:16.5, type:"finfet", features:"WORLD'S FIRST commercial FinFET", chips:"Ivy Bridge, Haswell" }, | |
| { name:"14nm", year:2014, density:44.7, type:"finfet", features:"Extremely long-lived (2014-2021)", chips:"Broadwell through Comet Lake" }, | |
| { name:"Intel 7", year:2019, density:100.8, type:"finfet", altName:"10nm ESF", features:"Notoriously delayed (targeted 2016)", chips:"Alder Lake, Raptor Lake" }, | |
| { name:"Intel 4", year:2023, density:160.0, type:"finfet", altName:"ex-7nm", features:"Intel's first EUV node", chips:"Meteor Lake compute tile" }, | |
| { name:"Intel 3", year:2024, density:175.0, type:"finfet", features:"Last Intel FinFET", chips:"Granite Rapids, Sierra Forest" }, | |
| { name:"Intel 18A", year:2025, density:238.0, type:"gaa", features:"RibbonFET + PowerVia (backside power)", chips:"Panther Lake, Clearwater Forest" }, | |
| ]; | |
| const nmData = [ | |
| { node:"350nm", marketing:350, actual:350 }, | |
| { node:"250nm", marketing:250, actual:250 }, | |
| { node:"180nm", marketing:180, actual:130 }, | |
| { node:"130nm", marketing:130, actual:70 }, | |
| { node:"90nm", marketing:90, actual:50 }, | |
| { node:"65nm", marketing:65, actual:35 }, | |
| { node:"45nm", marketing:45, actual:25 }, | |
| { node:"28nm", marketing:28, actual:20 }, | |
| { node:"14nm", marketing:14, actual:14 }, | |
| { node:"7nm", marketing:7, actual:20 }, | |
| { node:"5nm", marketing:5, actual:18 }, | |
| { node:"3nm", marketing:3, actual:16 }, | |
| { node:"2nm", marketing:2, actual:12 }, | |
| ]; | |
| const milestones = [ | |
| { year:1998, text:"TSMC 250nm production on 200mm wafers", highlight:true }, | |
| { year:1999, text:"TSMC 180nm; copper interconnects become industry standard", highlight:true }, | |
| { year:2004, text:"TSMC 90nm; transition to 300mm wafers", highlight:true }, | |
| { year:2006, text:"ASML ships first EUV demonstration tools", highlight:false }, | |
| { year:2011, text:"TSMC 28nm -- the last major planar node, still in production today", highlight:true }, | |
| { year:2012, text:"Intel 22nm: the world's first commercial FinFET transistor (Ivy Bridge)", highlight:false }, | |
| { year:2015, text:"TSMC 16nm FinFET & Samsung 14nm FinFET enter production", highlight:true }, | |
| { year:2017, text:"TSMC 10nm (Apple A11); Samsung 10nm (Snapdragon 835)", highlight:true }, | |
| { year:2018, text:"TSMC 7nm -- massive node. Samsung first EUV. GlobalFoundries halts 7nm.", highlight:true }, | |
| { year:2019, text:"TSMC N7+ (first TSMC EUV process); SMIC achieves 14nm FinFET", highlight:true }, | |
| { year:2020, text:"TSMC 5nm (N5): Apple A14, Apple M1 launch. Full EUV adoption.", highlight:true }, | |
| { year:2021, text:"Intel 7 (10nm ESF) finally ships at volume with Alder Lake", highlight:false }, | |
| { year:2022, text:"Samsung ships world's first GAA transistors at 3nm; TSMC ships N3 (still FinFET)", highlight:true }, | |
| { year:2023, text:"SMIC N+2 stuns industry: 7nm Kirin 9000s without EUV. Intel 4 ships (Meteor Lake).", highlight:false }, | |
| { year:2024, text:"Intel 3 ships; ASML delivers first High-NA EUV tool to Intel", highlight:false }, | |
| { year:2025, text:"TSMC N2: first TSMC GAA nanosheet node enters production. Intel 18A risk production.", highlight:true }, | |
| { year:2026, text:"TSMC N2 ramp to 100k wafers/month; N2P (H2); Intel 18A volume (Clearwater Forest)", highlight:true }, | |
| ]; | |
| const euvTimeline = [ | |
| { label:"Samsung 7nm", year:"2018", color:"var(--samsung)", note:"First EUV production" }, | |
| { label:"TSMC N7+", year:"2019", color:"var(--tsmc)", note:"First TSMC EUV" }, | |
| { label:"TSMC N5", year:"2020", color:"var(--tsmc)", note:"~14 EUV layers" }, | |
| { label:"TSMC N3", year:"2022", color:"var(--tsmc)", note:"Extensive EUV" }, | |
| { label:"Intel 4", year:"2023", color:"var(--intel)", note:"First Intel EUV" }, | |
| { label:"High-NA EUV", year:"2024", color:"var(--accent2)", note:"Next-gen tools ship" }, | |
| { label:"TSMC N2", year:"2025", color:"var(--tsmc)", note:"GAA + EUV" }, | |
| ]; | |
| // ==================== HERO TICKER ==================== | |
| const tickerEl = document.getElementById('nodeTicker'); | |
| const tickerNodes = ["250nm","180nm","130nm","90nm","65nm","40nm","28nm","20nm","16nm","10nm","7nm","5nm","3nm","2nm"]; | |
| tickerNodes.forEach((n, i) => { | |
| const span = document.createElement('span'); | |
| span.textContent = n; | |
| tickerEl.appendChild(span); | |
| }); | |
| let tickerIndex = 0; | |
| function animateTicker() { | |
| const spans = tickerEl.querySelectorAll('span'); | |
| spans.forEach(s => s.classList.remove('active')); | |
| spans[tickerIndex].classList.add('active'); | |
| tickerIndex = (tickerIndex + 1) % spans.length; | |
| } | |
| animateTicker(); | |
| setInterval(animateTicker, 800); | |
| // ==================== NM BARS ==================== | |
| const nmBarsEl = document.getElementById('nmBars'); | |
| const maxNm = 350; | |
| nmData.forEach(d => { | |
| const group = document.createElement('div'); | |
| group.className = 'nm-bar-group'; | |
| const stack = document.createElement('div'); | |
| stack.className = 'nm-bar-stack'; | |
| const mBar = document.createElement('div'); | |
| mBar.className = 'nm-bar marketing'; | |
| const mH = Math.max((d.marketing / maxNm) * 180, 4); | |
| mBar.style.height = mH + 'px'; | |
| const mVal = document.createElement('div'); | |
| mVal.className = 'nm-bar-value'; | |
| mVal.textContent = d.marketing + 'nm'; | |
| mBar.appendChild(mVal); | |
| const aBar = document.createElement('div'); | |
| aBar.className = 'nm-bar actual'; | |
| const aH = Math.max((d.actual / maxNm) * 180, 4); | |
| aBar.style.height = aH + 'px'; | |
| stack.appendChild(mBar); | |
| if (d.actual !== d.marketing) { | |
| const aBarInner = document.createElement('div'); | |
| aBarInner.className = 'nm-bar actual'; | |
| aBarInner.style.height = aH + 'px'; | |
| aBarInner.style.position = 'absolute'; | |
| aBarInner.style.bottom = '0'; | |
| aBarInner.style.width = '60%'; | |
| aBarInner.style.left = '20%'; | |
| const aVal = document.createElement('div'); | |
| aVal.className = 'nm-bar-value'; | |
| aVal.textContent = d.actual + 'nm'; | |
| aVal.style.color = 'var(--tsmc)'; | |
| // overlay on marketing bar | |
| } | |
| const label = document.createElement('div'); | |
| label.className = 'nm-bar-label'; | |
| label.textContent = d.node; | |
| group.appendChild(stack); | |
| group.appendChild(label); | |
| // add actual bar overlay | |
| if (d.marketing !== d.actual) { | |
| const overlay = document.createElement('div'); | |
| overlay.style.cssText = `width:50%;height:${aH}px;background:linear-gradient(180deg,rgba(225,29,72,0.7),rgba(225,29,72,0.3));border:1px solid rgba(225,29,72,0.5);border-radius:3px;position:absolute;bottom:0;left:25%;`; | |
| stack.style.position = 'relative'; | |
| stack.appendChild(overlay); | |
| } | |
| nmBarsEl.appendChild(group); | |
| }); | |
| // ==================== TRANSISTOR TYPES ==================== | |
| const transistorTypesEl = document.getElementById('transistorTypes'); | |
| const types = [ | |
| { | |
| name: "Planar MOSFET", | |
| era: "1960s - 2014", | |
| badge: "planar", | |
| gateCoverage: 25, | |
| desc: "The gate sits on top of a flat silicon channel. Simple and cheap, but at 20nm and below, the gate loses electrostatic control — current leaks between source and drain even when the transistor is \"off.\"", | |
| svg: `<svg viewBox="0 0 220 150" xmlns="http://www.w3.org/2000/svg"> | |
| <defs><linearGradient id="siGrad" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#475569"/><stop offset="100%" stop-color="#334155"/></linearGradient></defs> | |
| <rect x="10" y="80" width="200" height="60" rx="4" fill="url(#siGrad)" stroke="#64748b" stroke-width="1"/> | |
| <text x="110" y="115" text-anchor="middle" fill="#94a3b8" font-size="11" font-weight="600">Silicon Substrate</text> | |
| <rect x="30" y="60" width="40" height="20" rx="2" fill="#0ea5e9" stroke="#38bdf8" stroke-width="1"/> | |
| <text x="50" y="74" text-anchor="middle" fill="#fff" font-size="8" font-weight="700">Source</text> | |
| <rect x="150" y="60" width="40" height="20" rx="2" fill="#0ea5e9" stroke="#38bdf8" stroke-width="1"/> | |
| <text x="170" y="74" text-anchor="middle" fill="#fff" font-size="8" font-weight="700">Drain</text> | |
| <rect x="80" y="40" width="60" height="20" rx="2" fill="#e11d48" stroke="#fb7185" stroke-width="1"/> | |
| <text x="110" y="54" text-anchor="middle" fill="#fff" font-size="8" font-weight="700">Gate</text> | |
| <line x1="80" y1="60" x2="140" y2="60" stroke="#f59e0b" stroke-width="2" stroke-dasharray="3,2"/> | |
| <text x="110" y="77" text-anchor="middle" fill="#f59e0b" font-size="7">Channel</text> | |
| <text x="110" y="25" text-anchor="middle" fill="#94a3b8" font-size="9">Gate on 1 side (top)</text> | |
| <path d="M 85 38 L 85 30 L 135 30 L 135 38" fill="none" stroke="#94a3b8" stroke-width="0.75"/> | |
| </svg>` | |
| }, | |
| { | |
| name: "FinFET", | |
| era: "2012 - 2025", | |
| badge: "finfet", | |
| gateCoverage: 75, | |
| desc: "The silicon channel becomes a vertical \"fin\" with the gate wrapped around three sides. This dramatically improved gate control, reducing leakage by ~50%. Intel pioneered this at 22nm in 2012; TSMC adopted it at 16nm in 2015.", | |
| svg: `<svg viewBox="0 0 220 150" xmlns="http://www.w3.org/2000/svg"> | |
| <defs><linearGradient id="siGrad2" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#475569"/><stop offset="100%" stop-color="#334155"/></linearGradient></defs> | |
| <rect x="10" y="100" width="200" height="40" rx="4" fill="url(#siGrad2)" stroke="#64748b" stroke-width="1"/> | |
| <text x="110" y="124" text-anchor="middle" fill="#94a3b8" font-size="11" font-weight="600">Silicon Substrate</text> | |
| <rect x="95" y="40" width="30" height="60" rx="2" fill="#0ea5e9" stroke="#38bdf8" stroke-width="1"/> | |
| <text x="110" y="75" text-anchor="middle" fill="#fff" font-size="8" font-weight="700" transform="rotate(-90 110 75)">Fin</text> | |
| <path d="M 75 35 L 75 90 L 85 90 L 85 45 L 135 45 L 135 90 L 145 90 L 145 35 Z" fill="rgba(225,29,72,0.7)" stroke="#fb7185" stroke-width="1"/> | |
| <text x="110" y="30" text-anchor="middle" fill="#e11d48" font-size="9" font-weight="700">Gate</text> | |
| <rect x="40" y="70" width="45" height="30" rx="2" fill="rgba(14,165,233,0.3)" stroke="#38bdf8" stroke-width="0.75" stroke-dasharray="2,2"/> | |
| <text x="62" y="89" text-anchor="middle" fill="#38bdf8" font-size="7" font-weight="600">Source</text> | |
| <rect x="135" y="70" width="45" height="30" rx="2" fill="rgba(14,165,233,0.3)" stroke="#38bdf8" stroke-width="0.75" stroke-dasharray="2,2"/> | |
| <text x="157" y="89" text-anchor="middle" fill="#38bdf8" font-size="7" font-weight="600">Drain</text> | |
| <text x="110" y="142" text-anchor="middle" fill="#94a3b8" font-size="9">Gate on 3 sides</text> | |
| </svg>` | |
| }, | |
| { | |
| name: "GAA Nanosheet", | |
| era: "2022 - present", | |
| badge: "gaa", | |
| gateCoverage: 100, | |
| desc: "Horizontal stacked nanosheets with the gate completely surrounding each sheet on all four sides. Provides the best electrostatic control and tunable drive current by varying sheet width. Samsung shipped first (2022); TSMC adopted it at N2 (2025).", | |
| svg: `<svg viewBox="0 0 220 150" xmlns="http://www.w3.org/2000/svg"> | |
| <defs><linearGradient id="siGrad3" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#475569"/><stop offset="100%" stop-color="#334155"/></linearGradient></defs> | |
| <rect x="10" y="115" width="200" height="30" rx="4" fill="url(#siGrad3)" stroke="#64748b" stroke-width="1"/> | |
| <text x="110" y="134" text-anchor="middle" fill="#94a3b8" font-size="10" font-weight="600">Substrate</text> | |
| <rect x="70" y="25" width="80" height="85" rx="6" fill="rgba(225,29,72,0.25)" stroke="#e11d48" stroke-width="1.5"/> | |
| <text x="110" y="20" text-anchor="middle" fill="#e11d48" font-size="9" font-weight="700">Gate (all 4 sides)</text> | |
| <rect x="85" y="35" width="50" height="12" rx="3" fill="#0ea5e9" stroke="#38bdf8" stroke-width="1"/> | |
| <text x="110" y="44" text-anchor="middle" fill="#fff" font-size="7" font-weight="600">Sheet 1</text> | |
| <rect x="85" y="55" width="50" height="12" rx="3" fill="#0ea5e9" stroke="#38bdf8" stroke-width="1"/> | |
| <text x="110" y="64" text-anchor="middle" fill="#fff" font-size="7" font-weight="600">Sheet 2</text> | |
| <rect x="85" y="75" width="50" height="12" rx="3" fill="#0ea5e9" stroke="#38bdf8" stroke-width="1"/> | |
| <text x="110" y="84" text-anchor="middle" fill="#fff" font-size="7" font-weight="600">Sheet 3</text> | |
| <rect x="30" y="45" width="30" height="55" rx="2" fill="rgba(14,165,233,0.3)" stroke="#38bdf8" stroke-width="0.75" stroke-dasharray="2,2"/> | |
| <text x="45" y="76" text-anchor="middle" fill="#38bdf8" font-size="7" font-weight="600">Source</text> | |
| <rect x="160" y="45" width="30" height="55" rx="2" fill="rgba(14,165,233,0.3)" stroke="#38bdf8" stroke-width="0.75" stroke-dasharray="2,2"/> | |
| <text x="175" y="76" text-anchor="middle" fill="#38bdf8" font-size="7" font-weight="600">Drain</text> | |
| <text x="110" y="100" text-anchor="middle" fill="#8b5cf6" font-size="8" font-weight="600">Gate wraps ALL sheets</text> | |
| </svg>` | |
| }, | |
| ]; | |
| types.forEach(t => { | |
| const card = document.createElement('div'); | |
| card.className = 'transistor-card'; | |
| const coverageColor = t.badge === 'planar' ? '#f59e0b' : t.badge === 'finfet' ? '#3b82f6' : '#8b5cf6'; | |
| card.innerHTML = ` | |
| <span class="era-badge">${t.era}</span> | |
| <h3>${t.name}</h3> | |
| <div class="transistor-svg-container">${t.svg}</div> | |
| <p class="description">${t.desc}</p> | |
| <div class="gate-coverage"> | |
| <span>Gate coverage:</span> | |
| <div class="coverage-bar"> | |
| <div class="coverage-fill" style="width:${t.gateCoverage}%;background:${coverageColor}"></div> | |
| </div> | |
| <span>${t.gateCoverage}%</span> | |
| </div> | |
| `; | |
| transistorTypesEl.appendChild(card); | |
| }); | |
| // ==================== TIMELINE ==================== | |
| const timelineControlsEl = document.getElementById('timelineControls'); | |
| const timelineNodesEl = document.getElementById('timelineNodes'); | |
| const foundryFilters = [ | |
| { id:'all', label:'All Foundries' }, | |
| { id:'tsmc', label:'TSMC', color:'var(--tsmc)' }, | |
| { id:'samsung', label:'Samsung', color:'var(--samsung)' }, | |
| { id:'intel', label:'Intel', color:'var(--intel)' }, | |
| ]; | |
| let activeFilter = 'all'; | |
| foundryFilters.forEach(f => { | |
| const btn = document.createElement('button'); | |
| btn.textContent = f.label; | |
| btn.className = f.id === 'all' ? 'active' : ''; | |
| btn.onclick = () => { | |
| activeFilter = f.id; | |
| timelineControlsEl.querySelectorAll('button').forEach(b => b.classList.remove('active')); | |
| btn.classList.add('active'); | |
| renderTimeline(); | |
| }; | |
| timelineControlsEl.appendChild(btn); | |
| }); | |
| function renderTimeline() { | |
| timelineNodesEl.innerHTML = ''; | |
| let nodes = []; | |
| if (activeFilter === 'all' || activeFilter === 'tsmc') { | |
| tsmcNodes.forEach(n => nodes.push({ ...n, foundry:'TSMC', cls:'', color:'var(--tsmc)' })); | |
| } | |
| if (activeFilter === 'all' || activeFilter === 'samsung') { | |
| samsungNodes.forEach(n => nodes.push({ ...n, foundry:'Samsung', cls:'samsung-dot', color:'var(--samsung)' })); | |
| } | |
| if (activeFilter === 'all' || activeFilter === 'intel') { | |
| intelNodes.forEach(n => nodes.push({ ...n, foundry:'Intel', cls:'intel-dot', color:'var(--intel)' })); | |
| } | |
| nodes.sort((a, b) => a.year - b.year || a.foundry.localeCompare(b.foundry)); | |
| nodes.forEach((n, i) => { | |
| const el = document.createElement('div'); | |
| el.className = `timeline-node ${n.cls}`; | |
| const typeBadge = n.type === 'planar' ? 'planar' : n.type === 'finfet' ? 'finfet' : 'gaa'; | |
| const typeLabel = n.type === 'planar' ? 'Planar' : n.type === 'finfet' ? 'FinFET' : 'GAA'; | |
| el.innerHTML = ` | |
| <div class="node-label" style="color:${n.color}">${n.foundry === 'TSMC' ? '' : n.foundry + ' '}${n.name}</div> | |
| <div class="dot" style="border-color:${n.color}"></div> | |
| <div class="node-year">${n.year}</div> | |
| <div class="node-detail"> | |
| <span class="type-badge ${typeBadge}">${typeLabel}</span> | |
| <h4>${n.foundry} ${n.name}</h4> | |
| <div class="detail-row"><span class="label">Year</span><span class="value">${n.year}</span></div> | |
| <div class="detail-row"><span class="label">Density</span><span class="value">${n.density ? n.density + ' MTr/mm\u00B2' : 'N/A'}</span></div> | |
| ${n.litho ? `<div class="detail-row"><span class="label">Lithography</span><span class="value">${n.litho}</span></div>` : ''} | |
| <div class="detail-row"><span class="label">Key Feature</span><span class="value" style="text-align:right;max-width:160px">${n.features}</span></div> | |
| <div class="chips"><strong>Notable chips:</strong> ${n.chips}</div> | |
| </div> | |
| `; | |
| el.onclick = (e) => { | |
| e.stopPropagation(); | |
| const wasExpanded = el.classList.contains('expanded'); | |
| timelineNodesEl.querySelectorAll('.timeline-node').forEach(n => n.classList.remove('expanded')); | |
| if (!wasExpanded) el.classList.add('expanded'); | |
| }; | |
| timelineNodesEl.appendChild(el); | |
| }); | |
| } | |
| renderTimeline(); | |
| document.addEventListener('click', () => { | |
| timelineNodesEl.querySelectorAll('.timeline-node').forEach(n => n.classList.remove('expanded')); | |
| }); | |
| // ==================== DENSITY CHART (SVG) ==================== | |
| const chartEl = document.getElementById('densityChart'); | |
| const legendEl = document.getElementById('chartLegend'); | |
| const chartData = { | |
| tsmc: { color:'#e11d48', label:'TSMC', data: tsmcNodes.filter(n=>n.density).map(n=>({x:n.year,y:n.density,label:n.name})) }, | |
| samsung: { color:'#2563eb', label:'Samsung', data: samsungNodes.filter(n=>n.density).map(n=>({x:n.year,y:n.density,label:n.name})) }, | |
| intel: { color:'#0ea5e9', label:'Intel', data: intelNodes.filter(n=>n.density).map(n=>({x:n.year,y:n.density,label:n.name})) }, | |
| }; | |
| let visibleSeries = { tsmc: true, samsung: true, intel: true }; | |
| function renderLegend() { | |
| legendEl.innerHTML = ''; | |
| Object.entries(chartData).forEach(([key, series]) => { | |
| const item = document.createElement('div'); | |
| item.className = `legend-item ${visibleSeries[key] ? '' : 'dimmed'}`; | |
| item.innerHTML = `<span class="legend-dot" style="background:${series.color}"></span>${series.label}`; | |
| item.onclick = () => { | |
| visibleSeries[key] = !visibleSeries[key]; | |
| renderLegend(); | |
| renderDensityChart(); | |
| }; | |
| legendEl.appendChild(item); | |
| }); | |
| } | |
| function renderDensityChart() { | |
| const W = 700, H = 380; | |
| const pad = { top: 20, right: 30, bottom: 50, left: 65 }; | |
| const cW = W - pad.left - pad.right; | |
| const cH = H - pad.top - pad.bottom; | |
| // Gather all visible points | |
| let allPts = []; | |
| Object.entries(chartData).forEach(([key, s]) => { | |
| if (visibleSeries[key]) allPts = allPts.concat(s.data); | |
| }); | |
| if (allPts.length === 0) allPts = [{ x: 2000, y: 1 }]; | |
| const minYear = 1997, maxYear = 2026; | |
| const minDensity = 0.3, maxDensity = 400; | |
| const logMin = Math.log10(minDensity), logMax = Math.log10(maxDensity); | |
| function xScale(year) { return pad.left + ((year - minYear) / (maxYear - minYear)) * cW; } | |
| function yScale(density) { return pad.top + cH - ((Math.log10(density) - logMin) / (logMax - logMin)) * cH; } | |
| let svg = `<svg viewBox="0 0 ${W} ${H}" xmlns="http://www.w3.org/2000/svg">`; | |
| // Grid lines (horizontal - log scale) | |
| const gridValues = [0.5, 1, 2, 5, 10, 20, 50, 100, 200, 400]; | |
| gridValues.forEach(v => { | |
| const y = yScale(v); | |
| if (y > pad.top && y < pad.top + cH) { | |
| svg += `<line x1="${pad.left}" y1="${y}" x2="${pad.left+cW}" y2="${y}" stroke="#1e293b" stroke-width="1"/>`; | |
| svg += `<text x="${pad.left-8}" y="${y+4}" text-anchor="end" fill="#64748b" font-size="10">${v >= 1 ? v : v.toFixed(1)}</text>`; | |
| } | |
| }); | |
| // Grid lines (vertical - years) | |
| for (let yr = 2000; yr <= 2026; yr += 2) { | |
| const x = xScale(yr); | |
| svg += `<line x1="${x}" y1="${pad.top}" x2="${x}" y2="${pad.top+cH}" stroke="#1e293b" stroke-width="1"/>`; | |
| svg += `<text x="${x}" y="${pad.top+cH+18}" text-anchor="middle" fill="#64748b" font-size="10">${yr}</text>`; | |
| } | |
| // Axis labels | |
| svg += `<text x="${pad.left + cW/2}" y="${H-5}" text-anchor="middle" fill="#64748b" font-size="11">Year</text>`; | |
| svg += `<text x="14" y="${pad.top + cH/2}" text-anchor="middle" fill="#64748b" font-size="11" transform="rotate(-90, 14, ${pad.top + cH/2})">MTr/mm\u00B2 (log)</text>`; | |
| // Axes | |
| svg += `<line x1="${pad.left}" y1="${pad.top}" x2="${pad.left}" y2="${pad.top+cH}" stroke="#334155" stroke-width="1.5"/>`; | |
| svg += `<line x1="${pad.left}" y1="${pad.top+cH}" x2="${pad.left+cW}" y2="${pad.top+cH}" stroke="#334155" stroke-width="1.5"/>`; | |
| // Series | |
| Object.entries(chartData).forEach(([key, series]) => { | |
| if (!visibleSeries[key]) return; | |
| const pts = series.data.sort((a, b) => a.x - b.x); | |
| if (pts.length < 2) return; | |
| // Line | |
| const pathPts = pts.map(p => `${xScale(p.x)},${yScale(p.y)}`).join(' L '); | |
| svg += `<path d="M ${pathPts}" fill="none" stroke="${series.color}" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" opacity="0.8"/>`; | |
| // Area fill | |
| const areaPath = `M ${xScale(pts[0].x)},${yScale(pts[0].y)} L ${pathPts} L ${xScale(pts[pts.length-1].x)},${pad.top+cH} L ${xScale(pts[0].x)},${pad.top+cH} Z`; | |
| svg += `<path d="${areaPath}" fill="${series.color}" opacity="0.06"/>`; | |
| // Dots + labels | |
| pts.forEach(p => { | |
| const cx = xScale(p.x), cy = yScale(p.y); | |
| svg += `<circle cx="${cx}" cy="${cy}" r="4" fill="${series.color}" stroke="var(--bg)" stroke-width="2"/>`; | |
| svg += `<text x="${cx}" y="${cy - 10}" text-anchor="middle" fill="${series.color}" font-size="8" font-weight="600">${p.label}</text>`; | |
| }); | |
| }); | |
| svg += `</svg>`; | |
| chartEl.innerHTML = svg; | |
| } | |
| renderLegend(); | |
| renderDensityChart(); | |
| // ==================== FOUNDRIES ==================== | |
| const foundryGridEl = document.getElementById('foundryGrid'); | |
| const foundries = [ | |
| { | |
| name: "TSMC", hq: "Hsinchu, Taiwan", color: "var(--tsmc)", | |
| leading: "2nm (N2)", year: "Est. 1987", density: "313", | |
| status: "Market leader", statusColor: "rgba(225,29,72,0.15)", statusText: "#e11d48", | |
| detail: "~60% global foundry market share. First TSMC GAA node (N2) in production Q4 2025. Supplies Apple, AMD, Nvidia, Qualcomm." | |
| }, | |
| { | |
| name: "Samsung", hq: "Hwaseong, South Korea", color: "var(--samsung)", | |
| leading: "3nm GAA", year: "Est. 1969", density: "170", | |
| status: "First GAA shipper", statusColor: "rgba(37,99,235,0.15)", statusText: "#2563eb", | |
| detail: "First to ship GAA transistors (2022) but yield issues (~50%) vs TSMC (~90%). Targeting 2nm GAA in 2025." | |
| }, | |
| { | |
| name: "Intel", hq: "Santa Clara, USA", color: "var(--intel)", | |
| leading: "Intel 18A", year: "Est. 1968", density: "238", | |
| status: "Foundry ambitions", statusColor: "rgba(14,165,233,0.15)", statusText: "#0ea5e9", | |
| detail: "Pioneered FinFET (2012). Intel 18A combines RibbonFET (GAA) + PowerVia (backside power). Opening fabs to external customers." | |
| }, | |
| { | |
| name: "GlobalFoundries", hq: "Malta, NY, USA", color: "var(--gf)", | |
| leading: "12nm FinFET", year: "Est. 2009", density: "~33", | |
| status: "Specialty focus", statusColor: "rgba(16,185,129,0.15)", statusText: "#10b981", | |
| detail: "Halted 7nm development in 2018. Focused on specialty: 22FDX FD-SOI for IoT, automotive, RF/5G. Profitable niche strategy." | |
| }, | |
| { | |
| name: "SMIC", hq: "Shanghai, China", color: "var(--smic)", | |
| leading: "N+2 (7nm-class)", year: "Est. 2000", density: "~91", | |
| status: "EUV-restricted", statusColor: "rgba(245,158,11,0.15)", statusText: "#f59e0b", | |
| detail: "Achieved 7nm without EUV (Kirin 9000s, 2023). Capped at ~7nm class due to export controls blocking EUV access. Uses DUV multi-patterning." | |
| }, | |
| ]; | |
| foundries.forEach(f => { | |
| const card = document.createElement('div'); | |
| card.className = 'foundry-card'; | |
| card.style.borderColor = f.color; | |
| card.innerHTML = ` | |
| <div class="foundry-name" style="color:${f.color}">${f.name}</div> | |
| <div class="foundry-hq">${f.hq} · ${f.year}</div> | |
| <div class="stat"> | |
| <div class="stat-label">Leading Node</div> | |
| <div class="stat-value">${f.leading}</div> | |
| </div> | |
| <div class="stat"> | |
| <div class="stat-label">Peak Density (MTr/mm²)</div> | |
| <div class="stat-value">${f.density}</div> | |
| </div> | |
| <span class="status-pill" style="background:${f.statusColor};color:${f.statusText}">${f.status}</span> | |
| <p style="margin-top:1rem;font-size:0.82rem;color:var(--text-dim);">${f.detail}</p> | |
| `; | |
| foundryGridEl.appendChild(card); | |
| }); | |
| // ==================== EUV TIMELINE ==================== | |
| const euvTimelineEl = document.getElementById('euvTimeline'); | |
| euvTimeline.forEach(e => { | |
| const step = document.createElement('div'); | |
| step.className = 'euv-step'; | |
| step.innerHTML = ` | |
| <div class="euv-label" style="margin-bottom:2.5rem;font-size:0.75rem;color:var(--text-dim)">${e.note}</div> | |
| <div class="euv-dot" style="background:${e.color}"></div> | |
| <div class="euv-label">${e.label}</div> | |
| <div class="euv-year">${e.year}</div> | |
| `; | |
| euvTimelineEl.appendChild(step); | |
| }); | |
| // ==================== MOORE'S LAW CHART ==================== | |
| const mooresEl = document.getElementById('mooresChart'); | |
| function renderMooresChart() { | |
| const pts = tsmcNodes.filter(n => n.density).map(n => ({ x: n.year, y: n.density, label: n.name })); | |
| const W = 500, H = 340; | |
| const pad = { top: 20, right: 20, bottom: 45, left: 55 }; | |
| const cW = W - pad.left - pad.right; | |
| const cH = H - pad.top - pad.bottom; | |
| const minYear = 1997, maxYear = 2026; | |
| const logMin = Math.log10(0.3), logMax = Math.log10(400); | |
| function xS(yr) { return pad.left + ((yr - minYear) / (maxYear - minYear)) * cW; } | |
| function yS(d) { return pad.top + cH - ((Math.log10(d) - logMin) / (logMax - logMin)) * cH; } | |
| let svg = `<svg viewBox="0 0 ${W} ${H}" xmlns="http://www.w3.org/2000/svg">`; | |
| // Moore's Law reference line (doubling every 2 years from 0.4 in 1998) | |
| const mooreStart = 0.4; | |
| const mooreYear0 = 1998; | |
| let moorePts = []; | |
| for (let yr = mooreYear0; yr <= 2026; yr += 0.5) { | |
| const d = mooreStart * Math.pow(2, (yr - mooreYear0) / 2); | |
| if (d <= 500) moorePts.push({ x: yr, y: d }); | |
| } | |
| const moorePath = moorePts.map((p, i) => `${i === 0 ? 'M' : 'L'} ${xS(p.x)},${yS(p.y)}`).join(' '); | |
| svg += `<path d="${moorePath}" fill="none" stroke="#f59e0b" stroke-width="1.5" stroke-dasharray="6,4" opacity="0.5"/>`; | |
| svg += `<text x="${xS(2015)}" y="${yS(mooreStart * Math.pow(2, (2015 - mooreYear0) / 2)) - 10}" fill="#f59e0b" font-size="9" opacity="0.7">Moore's Law (2x/2yr)</text>`; | |
| // Grid | |
| [0.5, 1, 5, 10, 50, 100, 300].forEach(v => { | |
| const y = yS(v); | |
| svg += `<line x1="${pad.left}" y1="${y}" x2="${pad.left+cW}" y2="${y}" stroke="#1e293b" stroke-width="1"/>`; | |
| svg += `<text x="${pad.left-6}" y="${y+4}" text-anchor="end" fill="#64748b" font-size="9">${v}</text>`; | |
| }); | |
| for (let yr = 2000; yr <= 2026; yr += 4) { | |
| const x = xS(yr); | |
| svg += `<line x1="${x}" y1="${pad.top}" x2="${x}" y2="${pad.top+cH}" stroke="#1e293b" stroke-width="1"/>`; | |
| svg += `<text x="${x}" y="${pad.top+cH+16}" text-anchor="middle" fill="#64748b" font-size="9">${yr}</text>`; | |
| } | |
| // Axes | |
| svg += `<line x1="${pad.left}" y1="${pad.top}" x2="${pad.left}" y2="${pad.top+cH}" stroke="#334155" stroke-width="1.5"/>`; | |
| svg += `<line x1="${pad.left}" y1="${pad.top+cH}" x2="${pad.left+cW}" y2="${pad.top+cH}" stroke="#334155" stroke-width="1.5"/>`; | |
| // Actual TSMC line | |
| const tsmcPath = pts.map((p, i) => `${i === 0 ? 'M' : 'L'} ${xS(p.x)},${yS(p.y)}`).join(' '); | |
| svg += `<path d="${tsmcPath}" fill="none" stroke="#e11d48" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>`; | |
| // Area | |
| const area = `${tsmcPath} L ${xS(pts[pts.length-1].x)},${pad.top+cH} L ${xS(pts[0].x)},${pad.top+cH} Z`; | |
| svg += `<path d="${area}" fill="#e11d48" opacity="0.08"/>`; | |
| // Points | |
| pts.forEach(p => { | |
| svg += `<circle cx="${xS(p.x)}" cy="${yS(p.y)}" r="3.5" fill="#e11d48" stroke="var(--bg)" stroke-width="1.5"/>`; | |
| // Only label select points to avoid clutter | |
| if (['250nm','28nm','7nm','5nm','3nm','2nm'].includes(p.label)) { | |
| svg += `<text x="${xS(p.x)}" y="${yS(p.y)-9}" text-anchor="middle" fill="#e11d48" font-size="8" font-weight="600">${p.label}</text>`; | |
| } | |
| }); | |
| svg += `</svg>`; | |
| mooresEl.innerHTML = svg; | |
| } | |
| renderMooresChart(); | |
| // ==================== MILESTONES ==================== | |
| const milestonesEl = document.getElementById('milestonesList'); | |
| milestones.forEach(m => { | |
| const el = document.createElement('div'); | |
| el.className = `milestone ${m.highlight ? 'highlight' : ''}`; | |
| el.innerHTML = ` | |
| <span class="m-year">${m.year}</span> | |
| <span class="m-text">${m.text}</span> | |
| `; | |
| milestonesEl.appendChild(el); | |
| }); | |
| // ==================== SCROLL ANIMATIONS ==================== | |
| const observer = new IntersectionObserver((entries) => { | |
| entries.forEach(e => { | |
| if (e.isIntersecting) e.target.classList.add('visible'); | |
| }); | |
| }, { threshold: 0.1 }); | |
| document.querySelectorAll('.fade-in').forEach(el => observer.observe(el)); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment