mirror of
https://github.com/mileswolfallen2/miles-web.git
synced 2026-09-08 11:23:17 +00:00
Refactor code structure for improved readability and maintainability
This commit is contained in:
@@ -321,6 +321,75 @@ html.js .card.featured:hover { transform: translate(-3px,-3px) !important; }
|
|||||||
<div class="grid-label">History — all experiments, newest first</div>
|
<div class="grid-label">History — all experiments, newest first</div>
|
||||||
<div class="cards" id="history">
|
<div class="cards" id="history">
|
||||||
|
|
||||||
|
<!-- redesign expression five -->
|
||||||
|
<a class="card" href="redesign expression five.html">
|
||||||
|
<div class="card-preview">
|
||||||
|
<iframe src="redesign expression five.html" title="redesign expression five preview" loading="lazy" scrolling="no" tabindex="-1" aria-hidden="true"></iframe>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="card-num">11</div>
|
||||||
|
<div class="card-name">Expression Five</div>
|
||||||
|
<p class="card-desc">
|
||||||
|
MILES LAND — the portfolio as a playable Game Boy. A full CSS handheld shell (D-pad, A/B, START/SELECT) drives a 160×144 four-shade DMG screen: boot splash, PRESS START title, menu navigation, dialogue-box about with typewriter text, projects as paginated level select, skills as RPG stat bars, and a save-point contact screen that copies the email to your clipboard. Keyboard or on-shell controls.
|
||||||
|
</p>
|
||||||
|
<div class="card-footer">
|
||||||
|
<div class="card-tags">
|
||||||
|
<span class="ctag ctag-g">Game Boy DMG</span>
|
||||||
|
<span class="ctag ctag-p">Playable</span>
|
||||||
|
<span class="ctag ctag-c">Pixel UI</span>
|
||||||
|
<span class="ctag ctag-y">Press Start 2P</span>
|
||||||
|
</div>
|
||||||
|
<span class="card-arrow">↗</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- redesign expression four -->
|
||||||
|
<a class="card" href="redesign expression four.html">
|
||||||
|
<div class="card-preview">
|
||||||
|
<iframe src="redesign expression four.html" title="redesign expression four preview" loading="lazy" scrolling="no" tabindex="-1" aria-hidden="true"></iframe>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="card-num">10</div>
|
||||||
|
<div class="card-name">Expression Four</div>
|
||||||
|
<p class="card-desc">
|
||||||
|
MILES_OS — the portfolio as an amber-phosphor terminal. BIOS boot sequence, a real working command prompt (help / skills / projects / open 4 / sudo hire-me), neofetch-style system panel with live session uptime, skill meters, clickable project table, CRT scanlines and flicker, WebAudio key blips, and three switchable phosphor themes via `theme green`.
|
||||||
|
</p>
|
||||||
|
<div class="card-footer">
|
||||||
|
<div class="card-tags">
|
||||||
|
<span class="ctag ctag-y">Terminal TUI</span>
|
||||||
|
<span class="ctag ctag-p">Interactive CLI</span>
|
||||||
|
<span class="ctag ctag-c">CRT phosphor</span>
|
||||||
|
<span class="ctag ctag-g">Easter eggs</span>
|
||||||
|
</div>
|
||||||
|
<span class="card-arrow">↗</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- redesign expression three -->
|
||||||
|
<a class="card" href="redesign expression three.html">
|
||||||
|
<div class="card-preview">
|
||||||
|
<iframe src="redesign expression three.html" title="redesign expression three preview" loading="lazy" scrolling="no" tabindex="-1" aria-hidden="true"></iframe>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="card-num">09</div>
|
||||||
|
<div class="card-name">Expression Three</div>
|
||||||
|
<p class="card-desc">
|
||||||
|
Modern simplistic maximalism — the page fluctuates between Swiss-quiet and neon-loud sections as you scroll, tracked by a live SOFT⇄LOUD volume meter. Anime.js v4 engine, animate.css hover flourishes, and ReactBits-style vanilla components (SplitText, Blur Text, Count Up, Magnet, Glare Hover, Click Spark, Scramble). Master LOUD toggle or press L.
|
||||||
|
</p>
|
||||||
|
<div class="card-footer">
|
||||||
|
<div class="card-tags">
|
||||||
|
<span class="ctag ctag-y">Soft ⇄ Loud</span>
|
||||||
|
<span class="ctag ctag-p">Anime.js v4</span>
|
||||||
|
<span class="ctag ctag-c">Animate.css</span>
|
||||||
|
<span class="ctag ctag-g">ReactBits-style</span>
|
||||||
|
</div>
|
||||||
|
<span class="card-arrow">↗</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
|
||||||
<!-- redesign expression one -->
|
<!-- redesign expression one -->
|
||||||
<a class="card" href="redesign expression one.html">
|
<a class="card" href="redesign expression one.html">
|
||||||
<div class="card-preview">
|
<div class="card-preview">
|
||||||
|
|||||||
@@ -0,0 +1,479 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>MILES LAND — a playable portfolio</title>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
:root{
|
||||||
|
--p0:#9bbc0f;--p1:#8bac0f;--p2:#306230;--p3:#0f380f;
|
||||||
|
--shell:#c8c4bb;--shell-dk:#a9a49a;--bezel:#454952;--btn:#8b2f5e;
|
||||||
|
}
|
||||||
|
*{margin:0;padding:0;box-sizing:border-box}
|
||||||
|
body{background:#e9e5da;font-family:'Press Start 2P',monospace;color:#3a382f;
|
||||||
|
min-height:100dvh;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:20px;padding:26px 14px}
|
||||||
|
.room{display:flex;flex-direction:column;align-items:center;gap:18px}
|
||||||
|
.gb{width:min(430px,94vw);background:var(--shell);border-radius:12px 12px 58px 12px;
|
||||||
|
padding:16px 16px 26px;border:1px solid var(--shell-dk);position:relative;
|
||||||
|
box-shadow:0 24px 50px -18px rgba(40,36,20,.45),inset 0 2px 0 rgba(255,255,255,.55),inset 0 -3px 0 rgba(0,0,0,.08)}
|
||||||
|
.gb-top{display:flex;align-items:center;gap:10px;margin-bottom:10px;color:#8d887c;font-size:7px;letter-spacing:.12em}
|
||||||
|
.gb-top::before,.gb-top::after{content:"";flex:1;height:2px;background:repeating-linear-gradient(90deg,var(--shell-dk) 0 6px,transparent 6px 10px)}
|
||||||
|
.bezel{background:var(--bezel);border-radius:10px 10px 42px 10px;padding:22px 26px 30px;
|
||||||
|
box-shadow:inset 0 3px 8px rgba(0,0,0,.5),inset 0 -1px 0 rgba(255,255,255,.08)}
|
||||||
|
.bezel-row{display:flex;justify-content:center;margin-bottom:9px}
|
||||||
|
.bezel-txt{color:#98a2ac;font-size:7px;letter-spacing:.28em;text-align:center}
|
||||||
|
.lcd-wrap{position:relative;background:#6b7355;padding:4px;border-radius:3px}
|
||||||
|
.lcd{aspect-ratio:160/144;background:var(--p0);overflow:hidden;position:relative}
|
||||||
|
.stage{width:160px;height:144px;transform-origin:0 0;position:absolute;top:4px;left:4px}
|
||||||
|
.lcd-grid{position:absolute;inset:0;z-index:30;pointer-events:none;
|
||||||
|
background:
|
||||||
|
repeating-linear-gradient(0deg,rgba(15,56,15,.09) 0 1px,transparent 1px 3px),
|
||||||
|
repeating-linear-gradient(90deg,rgba(15,56,15,.09) 0 1px,transparent 1px 3px)}
|
||||||
|
.glare{position:absolute;inset:0;z-index:31;pointer-events:none;
|
||||||
|
background:linear-gradient(115deg,rgba(255,255,255,.13) 0%,rgba(255,255,255,0) 32%)}
|
||||||
|
.battery{position:absolute;left:-16px;bottom:34px;display:flex;flex-direction:column;align-items:center;gap:4px}
|
||||||
|
.battery i{width:8px;height:8px;border-radius:50%;background:#e4485c;box-shadow:0 0 6px #e4485c}
|
||||||
|
.battery span{color:#98a2ac;font-size:5px;letter-spacing:.1em;writing-mode:vertical-rl}
|
||||||
|
.brand{margin-top:14px;text-align:center;font-style:italic;font-weight:700;font-size:15px;color:#33366b;letter-spacing:.02em}
|
||||||
|
.brand small{font-size:8px;vertical-align:super}
|
||||||
|
.controls{display:flex;justify-content:space-between;align-items:center;margin-top:18px;padding:0 6px}
|
||||||
|
.dpad{position:relative;width:108px;height:108px;filter:drop-shadow(0 3px 2px rgba(0,0,0,.25))}
|
||||||
|
.dpad::after{content:"";position:absolute;left:50%;top:50%;width:34px;height:34px;transform:translate(-50%,-50%);
|
||||||
|
background:radial-gradient(circle at 35% 30%,#3a3a40,#17171b);border-radius:50%}
|
||||||
|
.dpad button{position:absolute;width:36px;height:36px;border:none;background:#212127;cursor:pointer;
|
||||||
|
box-shadow:inset 0 2px 0 rgba(255,255,255,.14);touch-action:manipulation}
|
||||||
|
.dpad button:active{background:#000}
|
||||||
|
.dpad .u{left:36px;top:0;border-radius:5px 5px 0 0}
|
||||||
|
.dpad .d{left:36px;bottom:0;border-radius:0 0 5px 5px}
|
||||||
|
.dpad .l{left:0;top:36px;border-radius:5px 0 0 5px}
|
||||||
|
.dpad .r{right:0;top:36px;border-radius:0 5px 5px 0}
|
||||||
|
.ab{display:flex;gap:18px;transform:rotate(-20deg)}
|
||||||
|
.ab button{width:54px;height:54px;border-radius:50%;border:none;background:var(--btn);cursor:pointer;
|
||||||
|
color:#f2d7e4;font-family:inherit;font-size:11px;box-shadow:0 4px 0 #5e1d3c,inset 0 2px 0 rgba(255,255,255,.25);
|
||||||
|
touch-action:manipulation}
|
||||||
|
.ab button:active{transform:translateY(3px);box-shadow:0 1px 0 #5e1d3c}
|
||||||
|
.ab .wrap{display:flex;flex-direction:column;align-items:center;gap:6px}
|
||||||
|
.ab .lbl{font-size:8px;color:#8d887c;letter-spacing:.15em}
|
||||||
|
.startselect{display:flex;justify-content:center;gap:22px;margin-top:20px}
|
||||||
|
.ss{display:flex;flex-direction:column;align-items:center;gap:6px}
|
||||||
|
.ss button{width:54px;height:14px;border-radius:8px;border:none;background:#212127;cursor:pointer;transform:rotate(-20deg);
|
||||||
|
box-shadow:inset 0 2px 0 rgba(255,255,255,.14);touch-action:manipulation}
|
||||||
|
.ss button:active{background:#000}
|
||||||
|
.ss span{font-size:7px;color:#8d887c;letter-spacing:.15em}
|
||||||
|
.speaker{position:absolute;right:22px;bottom:64px;width:74px;height:44px;transform:rotate(-20deg);
|
||||||
|
background:repeating-linear-gradient(90deg,var(--shell-dk) 0 5px,transparent 5px 12px);border-radius:3px;opacity:.85}
|
||||||
|
.notes{max-width:430px;width:100%;display:flex;justify-content:space-between;gap:14px;flex-wrap:wrap;
|
||||||
|
font-family:ui-monospace,Menlo,monospace;font-size:11px;color:#77725f;line-height:1.7}
|
||||||
|
.notes a{color:#5a5540}
|
||||||
|
.scr{position:absolute;inset:0;display:none;color:var(--p3);font-size:8px;line-height:1.5}
|
||||||
|
.scr.active{display:block}
|
||||||
|
.pad{padding:10px}
|
||||||
|
.hdr{font-size:8px;letter-spacing:.06em;border-bottom:2px solid var(--p3);padding-bottom:5px;margin-bottom:8px;display:flex;justify-content:space-between}
|
||||||
|
.dimtxt{color:var(--p2)}
|
||||||
|
#scr-boot{background:var(--p3)}
|
||||||
|
#scr-boot .logo{position:absolute;left:0;right:0;top:52px;text-align:center;color:var(--p0);font-size:14px;letter-spacing:.1em}
|
||||||
|
#scr-boot .logo em{font-style:normal;font-size:7px;vertical-align:super}
|
||||||
|
.booted #scr-boot .logo{animation:drop .9s cubic-bezier(.3,1.4,.5,1) both}
|
||||||
|
@keyframes drop{from{transform:translateY(-46px)}to{transform:none}}
|
||||||
|
#scr-title{text-align:center}
|
||||||
|
.title-logo{margin-top:26px;display:inline-block;border:3px solid var(--p3);padding:10px 12px;background:var(--p1)}
|
||||||
|
.title-logo b{display:block;font-size:15px;letter-spacing:.04em}
|
||||||
|
.title-logo span{display:block;font-size:8px;margin-top:6px;color:var(--p2)}
|
||||||
|
.pressstart{position:absolute;left:0;right:0;bottom:34px;font-size:8px;animation:blink 1s steps(1) infinite}
|
||||||
|
@keyframes blink{50%{opacity:0}}
|
||||||
|
.copyright{position:absolute;left:0;right:0;bottom:12px;font-size:6px;color:var(--p2)}
|
||||||
|
.menu-list{list-style:none;margin-top:6px}
|
||||||
|
.menu-list li{padding:6px 4px;font-size:8px;display:flex;gap:6px;align-items:center}
|
||||||
|
.menu-list li.sel{background:var(--p3);color:var(--p0)}
|
||||||
|
.menu-list li.sel::before{content:"▶";font-size:7px}
|
||||||
|
.menu-hint{position:absolute;left:10px;right:10px;bottom:8px;font-size:6px;color:var(--p2);line-height:1.8}
|
||||||
|
.avatar{image-rendering:pixelated;display:grid}
|
||||||
|
.avatar i{display:block}
|
||||||
|
.about-top{display:flex;gap:10px;align-items:flex-start}
|
||||||
|
.about-id{font-size:7px;line-height:1.9;padding-top:4px}
|
||||||
|
.dialog{position:absolute;left:6px;right:6px;bottom:6px;height:56px;background:var(--p0);
|
||||||
|
border:3px solid var(--p3);border-radius:2px;padding:7px 8px;font-size:7px;line-height:1.75}
|
||||||
|
.dialog .cont{position:absolute;right:7px;bottom:4px;animation:blink 1s steps(1) infinite}
|
||||||
|
.lv-grid{display:grid;grid-template-columns:1fr 1fr;gap:5px;margin-top:2px}
|
||||||
|
.lv{border:2px solid var(--p3);padding:5px 6px;font-size:6px;line-height:1.6;min-height:30px}
|
||||||
|
.lv.sel{background:var(--p3);color:var(--p0)}
|
||||||
|
.lv b{display:block;font-size:7px;margin-bottom:2px}
|
||||||
|
.lv-info{position:absolute;left:6px;right:6px;bottom:6px;border:2px solid var(--p3);background:var(--p0);
|
||||||
|
padding:5px 7px;font-size:6px;line-height:1.7;min-height:30px}
|
||||||
|
.pg-ind{font-size:6px;color:var(--p2)}
|
||||||
|
.stat-row{display:grid;grid-template-columns:64px 1fr 26px;gap:5px;align-items:center;margin-bottom:7px;font-size:6px}
|
||||||
|
.bar{height:8px;border:2px solid var(--p3);background:var(--p0);position:relative}
|
||||||
|
.bar i{position:absolute;top:0;bottom:0;left:0;background:var(--p2);width:0;transition:width .7s steps(12)}
|
||||||
|
.quest{border:2px solid var(--p3);padding:6px 7px;margin-bottom:6px;font-size:6px;line-height:1.7}
|
||||||
|
.quest b{font-size:7px;display:block;margin-bottom:2px}
|
||||||
|
.tag{float:right;background:var(--p3);color:var(--p0);padding:1px 4px;font-size:5px}
|
||||||
|
.ct-item{padding:6px 4px;font-size:7px;display:flex;justify-content:space-between;gap:6px}
|
||||||
|
.ct-item.sel{background:var(--p3);color:var(--p0)}
|
||||||
|
.toast{position:absolute;left:0;right:0;top:44px;text-align:center;font-size:8px;background:var(--p3);color:var(--p0);padding:6px 0;display:none}
|
||||||
|
.noscript-lcd{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;text-align:center;padding:14px;font-size:7px;line-height:2}
|
||||||
|
@media (prefers-reduced-motion:reduce){
|
||||||
|
*{animation:none!important;transition:none!important}
|
||||||
|
.bar i{width:var(--w)!important}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript><div style="font-family:monospace">MILES LAND needs JavaScript to boot. Human behind it: Miles Wolf Allen — [email protected]</div></noscript>
|
||||||
|
|
||||||
|
<div class="room">
|
||||||
|
<div class="gb">
|
||||||
|
<div class="gb-top"><span>DOT MATRIX WITH STEREO SOUND</span></div>
|
||||||
|
<div class="bezel">
|
||||||
|
<div class="bezel-row"><span class="bezel-txt">◄◄►</span></div>
|
||||||
|
<div class="lcd-wrap">
|
||||||
|
<div class="battery"><i></i><span>BATTERY</span></div>
|
||||||
|
<div class="lcd" id="lcd">
|
||||||
|
<div class="stage" id="stage">
|
||||||
|
|
||||||
|
<section class="scr active" id="scr-boot" aria-label="boot">
|
||||||
|
<noscript><div class="noscript-lcd">JS REQUIRED TO BOOT.<br>MILES WOLF ALLEN<br>[email protected]</div></noscript>
|
||||||
|
<div class="logo">MILES<em>™</em></div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="scr" id="scr-title" aria-label="title screen">
|
||||||
|
<div class="title-logo"><b>MILES</b><b>LAND</b><span>DEV QUEST</span></div>
|
||||||
|
<div class="pressstart">PRESS START</div>
|
||||||
|
<div class="copyright">©2026 MILES WOLF ALLEN</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="scr pad" id="scr-menu" aria-label="main menu">
|
||||||
|
<div class="hdr"><span>MAIN MENU</span><span class="pg-ind">v1.0</span></div>
|
||||||
|
<ul class="menu-list" id="menuList"></ul>
|
||||||
|
<div class="menu-hint">↑↓ SELECT · A CONFIRM<br>B BACK · ♪ = SELECT BTN</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="scr pad" id="scr-about" aria-label="about">
|
||||||
|
<div class="about-top">
|
||||||
|
<div class="avatar" id="avatar" aria-hidden="true"></div>
|
||||||
|
<div class="about-id">MILES W.<br>ALLEN<br><br>CLASS:<br>DEV<br>LVL 19</div>
|
||||||
|
</div>
|
||||||
|
<div class="dialog" id="dlg"><span id="dlgTxt"></span><span class="cont" id="dlgCont" hidden>▼</span></div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="scr pad" id="scr-projects" aria-label="projects">
|
||||||
|
<div class="hdr"><span>LEVEL SELECT</span><span class="pg-ind" id="pgInd">1/3</span></div>
|
||||||
|
<div class="lv-grid" id="lvGrid"></div>
|
||||||
|
<div class="lv-info" id="lvInfo"></div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="scr pad" id="scr-skills" aria-label="skills">
|
||||||
|
<div class="hdr"><span>STATS</span><span class="pg-ind">HP/MP</span></div>
|
||||||
|
<div id="statList"></div>
|
||||||
|
<div class="menu-hint">QUEUED: PHP · DOTNET · NIX · YARN</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="scr pad" id="scr-now" aria-label="now">
|
||||||
|
<div class="hdr"><span>QUEST LOG</span><span class="pg-ind">AUG 2026</span></div>
|
||||||
|
<div id="questList"></div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="scr pad" id="scr-contact" aria-label="contact">
|
||||||
|
<div class="hdr"><span>SAVE POINT</span><span class="pg-ind">♪ON</span></div>
|
||||||
|
<div id="ctList"></div>
|
||||||
|
<div class="menu-hint">A ON EMAIL = COPY · A ON LINK = OPEN</div>
|
||||||
|
<div class="toast" id="toast">COPIED!</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="lcd-grid" aria-hidden="true"></div>
|
||||||
|
<div class="glare" aria-hidden="true"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="brand">MilesLand<small>™</small></div>
|
||||||
|
<div class="controls">
|
||||||
|
<div class="dpad" aria-label="directional pad">
|
||||||
|
<button class="u" data-k="up" aria-label="up"></button>
|
||||||
|
<button class="l" data-k="left" aria-label="left"></button>
|
||||||
|
<button class="r" data-k="right" aria-label="right"></button>
|
||||||
|
<button class="d" data-k="down" aria-label="down"></button>
|
||||||
|
</div>
|
||||||
|
<div class="ab">
|
||||||
|
<div class="wrap"><button data-k="b" aria-label="B button">B</button><span class="lbl">BACK</span></div>
|
||||||
|
<div class="wrap"><button data-k="a" aria-label="A button">A</button><span class="lbl">OK</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="startselect">
|
||||||
|
<div class="ss"><button data-k="select" aria-label="select button"></button><span>SELECT</span></div>
|
||||||
|
<div class="ss"><button data-k="start" aria-label="start button"></button><span>START</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="speaker" aria-hidden="true"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<aside class="notes">
|
||||||
|
<span>KEYS: ARROWS · Z=A · X=B · ENTER=START</span>
|
||||||
|
<span><a href="./hub.html">← redesign lab</a> · <a href="../index.html">milesallen.site</a></span>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
"use strict";
|
||||||
|
const RM=matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||||||
|
const $=s=>document.querySelector(s);
|
||||||
|
const stage=$("#stage"),lcd=$("#lcd");
|
||||||
|
let sndOn=true,audioCtx=null;
|
||||||
|
|
||||||
|
function blip(f,d){
|
||||||
|
if(!sndOn)return;
|
||||||
|
try{
|
||||||
|
audioCtx=audioCtx||new (window.AudioContext||window.webkitAudioContext)();
|
||||||
|
if(audioCtx.state==="suspended")audioCtx.resume();
|
||||||
|
const o=audioCtx.createOscillator(),g=audioCtx.createGain();
|
||||||
|
o.type="square";o.frequency.value=f||720;
|
||||||
|
g.gain.setValueAtTime(.03,audioCtx.currentTime);
|
||||||
|
g.gain.exponentialRampToValueAtTime(.0001,audioCtx.currentTime+(d||.06));
|
||||||
|
o.connect(g).connect(audioCtx.destination);
|
||||||
|
o.start();o.stop(audioCtx.currentTime+(d||.06)+.02);
|
||||||
|
}catch(e){}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fit(){
|
||||||
|
const w=lcd.clientWidth-8,h=lcd.clientHeight-8;
|
||||||
|
stage.style.transform=`scale(${Math.min(w/160,h/144)})`;
|
||||||
|
}
|
||||||
|
addEventListener("resize",fit);
|
||||||
|
new ResizeObserver(fit).observe(lcd);
|
||||||
|
|
||||||
|
const SCREENS=["boot","title","menu","about","projects","skills","now","contact"];
|
||||||
|
let cur="boot";
|
||||||
|
function go(name){
|
||||||
|
cur=name;
|
||||||
|
SCREENS.forEach(s=>$("#scr-"+s).classList.toggle("active",s===name));
|
||||||
|
if(name==="skills")fillBars();
|
||||||
|
blip(name==="title"?520:880);
|
||||||
|
}
|
||||||
|
|
||||||
|
const MENU=[
|
||||||
|
{id:"about",label:"ABOUT"},
|
||||||
|
{id:"projects",label:"PROJECTS"},
|
||||||
|
{id:"skills",label:"SKILLS"},
|
||||||
|
{id:"now",label:"NOW"},
|
||||||
|
{id:"contact",label:"CONTACT"}
|
||||||
|
];
|
||||||
|
let mIdx=0;
|
||||||
|
const menuList=$("#menuList");
|
||||||
|
MENU.forEach((m,i)=>{
|
||||||
|
const li=document.createElement("li");
|
||||||
|
li.textContent=m.label;
|
||||||
|
li.addEventListener("click",()=>{mIdx=i;paintMenu();press("a");});
|
||||||
|
menuList.appendChild(li);
|
||||||
|
});
|
||||||
|
function paintMenu(){[...menuList.children].forEach((li,i)=>li.classList.toggle("sel",i===mIdx));}
|
||||||
|
paintMenu();
|
||||||
|
|
||||||
|
const AVATAR=[
|
||||||
|
"..33333333..",
|
||||||
|
".3222222223.",
|
||||||
|
"32333333323.",
|
||||||
|
"32333333323.",
|
||||||
|
"32222222223.",
|
||||||
|
"32333333323.",
|
||||||
|
".3222222223.",
|
||||||
|
"..33111133..",
|
||||||
|
"..31333313..",
|
||||||
|
"...333333..."
|
||||||
|
];
|
||||||
|
const SHADES={"0":"var(--p0)","1":"var(--p1)","2":"var(--p2)","3":"var(--p3)"};
|
||||||
|
(function drawAvatar(){
|
||||||
|
const el=$("#avatar");
|
||||||
|
el.style.gridTemplateColumns=`repeat(${AVATAR[0].length},4px)`;
|
||||||
|
AVATAR.join("").split("").forEach(ch=>{
|
||||||
|
const px=document.createElement("i");
|
||||||
|
px.style.width="4px";px.style.height="4px";
|
||||||
|
px.style.background=SHADES[ch]||"transparent";
|
||||||
|
el.appendChild(px);
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
|
const DLG=[
|
||||||
|
"HEY! I'M MILES. I BUILD BROWSERS, EMULATORS AND WEIRD WEB THINGS.",
|
||||||
|
"VANILLA IS MY MAIN QUEST: A BROWSER REBUILT FROM SCRATCH.",
|
||||||
|
"FEDL IS MY LIVE OPS GRIND: ~4000 RUNS VERIFIED.",
|
||||||
|
"PRESS B TO HEAD BACK. NOW GO PICK A LEVEL!"
|
||||||
|
];
|
||||||
|
let dlgI=0,typing=false,typeTimer=null;
|
||||||
|
function showLine(i){
|
||||||
|
const txt=DLG[i];const out=$("#dlgTxt");
|
||||||
|
$("#dlgCont").hidden=true;
|
||||||
|
if(RM){out.textContent=txt;typing=false;$("#dlgCont").hidden=false;return;}
|
||||||
|
typing=true;out.textContent="";let c=0;
|
||||||
|
clearInterval(typeTimer);
|
||||||
|
typeTimer=setInterval(()=>{
|
||||||
|
out.textContent+=txt[c++];
|
||||||
|
if(c>=txt.length){clearInterval(typeTimer);typing=false;$("#dlgCont").hidden=false;}
|
||||||
|
},26);
|
||||||
|
}
|
||||||
|
function advanceDlg(){
|
||||||
|
if(typing){clearInterval(typeTimer);$("#dlgTxt").textContent=DLG[dlgI];typing=false;$("#dlgCont").hidden=false;return;}
|
||||||
|
dlgI++;
|
||||||
|
if(dlgI>=DLG.length){go("menu");return;}
|
||||||
|
showLine(dlgI);
|
||||||
|
}
|
||||||
|
|
||||||
|
const PROJECTS=[
|
||||||
|
["01","OMNIEMU","ALL YOUR EMULATORS IN ONE PLACE.","https://milesallen.site/projects/omniemu.html"],
|
||||||
|
["02","VANILLA","A BROWSER BUILT TO BE REBUILT.","https://vanilla.milesallen.site"],
|
||||||
|
["03","GELECTRON","ELECTRON INTERNALS, DISSECTED.","https://milesallen.site/projects/gelectron.html"],
|
||||||
|
["04","FEDL","GD LEVEL LIST. 3800+ RUNS.","https://me.fedl.site"],
|
||||||
|
["05","MOON GAMING","COMMUNITY HUB. 1542 COMMITS.","https://github.com/mileswolfallen2"],
|
||||||
|
["06","NOTO","NOTES APP. BACK BURNER.","https://milesallen.site/projects/noto.html"],
|
||||||
|
["07","PORTFOLIO V3","STREET-POSTER ENERGY.","https://milesallen.site"],
|
||||||
|
["08","REDESIGN LAB","9+ DESIGN LANGUAGES.","https://milesallen.site/redesign/hub.html"],
|
||||||
|
["09","EMULATOR CORE","20+ CORES, ONE LAUNCHER.","https://milesallen.site/projects/omniemu.html"],
|
||||||
|
["10","GD TOOLS","DEMON LIST UTILITIES.","https://me.fedl.site"],
|
||||||
|
["11","SCHOOL UTILS","MAKE THE DAY SHORTER.","https://github.com/mileswolfallen2"],
|
||||||
|
["12","PIXEL PLAY","CANVAS + SHADER DOODLES.","https://codepen.io/mileswolfallen2-the-typescripter"],
|
||||||
|
["13","MILES LAND","YOU ARE PLAYING IT.",""]
|
||||||
|
];
|
||||||
|
const PER=6;
|
||||||
|
let pg=0,pSel=0;
|
||||||
|
const pgCount=Math.ceil(PROJECTS.length/PER);
|
||||||
|
const lvGrid=$("#lvGrid");
|
||||||
|
function paintLv(){
|
||||||
|
lvGrid.innerHTML="";
|
||||||
|
const start=pg*PER;
|
||||||
|
PROJECTS.slice(start,start+PER).forEach((p,i)=>{
|
||||||
|
const d=document.createElement("div");
|
||||||
|
d.className="lv"+(i===pSel?" sel":"");
|
||||||
|
d.innerHTML=`<b>${p[0]} ${p[1]}</b>${p[3]?"▶ OPEN":""}`;
|
||||||
|
d.addEventListener("click",()=>{pSel=i;paintLv();});
|
||||||
|
lvGrid.appendChild(d);
|
||||||
|
});
|
||||||
|
$("#pgInd").textContent=(pg+1)+"/"+pgCount;
|
||||||
|
const p=PROJECTS[start+pSel];
|
||||||
|
$("#lvInfo").textContent=p?p[0]+" "+p[1]+" — "+p[2]:"";
|
||||||
|
}
|
||||||
|
paintLv();
|
||||||
|
function openProject(){
|
||||||
|
const p=PROJECTS[pg*PER+pSel];
|
||||||
|
if(p&&p[3]){window.open(p[3],"_blank","noopener");blip(1040);}
|
||||||
|
else blip(220,.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
const SKILLS=[["JAVASCRIPT",90],["HTML/CSS",92],["TYPESCRIPT",82],["PYTHON",75],["SWIFT",65],["GIT",86],["NODE",78],["BLENDER",70]];
|
||||||
|
const statList=$("#statList");
|
||||||
|
SKILLS.forEach(([k,v])=>{
|
||||||
|
const row=document.createElement("div");
|
||||||
|
row.className="stat-row";
|
||||||
|
row.innerHTML=`<span>${k}</span><span class="bar"><i style="--w:${v}%"></i></span><span>${v}</span>`;
|
||||||
|
statList.appendChild(row);
|
||||||
|
});
|
||||||
|
function fillBars(){
|
||||||
|
requestAnimationFrame(()=>requestAnimationFrame(()=>{
|
||||||
|
statList.querySelectorAll(".bar i").forEach(b=>b.style.width=b.style.getPropertyValue("--w"));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
const QUESTS=[
|
||||||
|
["MAIN QUEST","VANILLA","browser rebuilt from scratch. active daily."],
|
||||||
|
["SIDE QUEST","GELECTRON","electron internals: window/ipc/menu/tray."],
|
||||||
|
["LIVE OPS","FEDL","~4000 runs verified across 340+ levels."],
|
||||||
|
["BACK BURNER","NOTO","waiting for its moment."]
|
||||||
|
];
|
||||||
|
QUESTS.forEach(([tag,t,d])=>{
|
||||||
|
const q=document.createElement("div");
|
||||||
|
q.className="quest";
|
||||||
|
q.innerHTML=`<span class="tag">${tag}</span><b>${t}</b>${d}`;
|
||||||
|
$("#questList").appendChild(q);
|
||||||
|
});
|
||||||
|
|
||||||
|
const CONTACT=[
|
||||||
|
["EMAIL","[email protected]","copy"],
|
||||||
|
["GITHUB","@mileswolfallen2","https://github.com/mileswolfallen2"],
|
||||||
|
["REPLIT","@MilesAllen1","https://replit.com/@MilesAllen1"],
|
||||||
|
["CODEPEN","typescripter","https://codepen.io/mileswolfallen2-the-typescripter"],
|
||||||
|
["FEDL","me.fedl.site","https://me.fedl.site"]
|
||||||
|
];
|
||||||
|
let ctIdx=0;
|
||||||
|
const ctList=$("#ctList");
|
||||||
|
CONTACT.forEach((c,i)=>{
|
||||||
|
const d=document.createElement("div");
|
||||||
|
d.className="ct-item";
|
||||||
|
d.innerHTML=`<span>${c[0]}</span><span>${c[1]}</span>`;
|
||||||
|
d.addEventListener("click",()=>{ctIdx=i;paintCt();press("a");});
|
||||||
|
ctList.appendChild(d);
|
||||||
|
});
|
||||||
|
function paintCt(){[...ctList.children].forEach((el,i)=>el.classList.toggle("sel",i===ctIdx));}
|
||||||
|
paintCt();
|
||||||
|
function actContact(){
|
||||||
|
const c=CONTACT[ctIdx];
|
||||||
|
if(c[2]==="copy"){
|
||||||
|
const done=()=>{const t=$("#toast");t.style.display="block";setTimeout(()=>t.style.display="none",1200);blip(1240);};
|
||||||
|
if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(c[1]).then(done,done);}
|
||||||
|
else done();
|
||||||
|
}else{window.open(c[2],"_blank","noopener");blip(1040);}
|
||||||
|
}
|
||||||
|
|
||||||
|
function press(k){
|
||||||
|
blip(k==="a"?980:k==="b"?420:700,.04);
|
||||||
|
if(cur==="boot"){go("title");return;}
|
||||||
|
if(cur==="title"){if(k==="start"||k==="a"){go("menu");}return;}
|
||||||
|
if(cur==="menu"){
|
||||||
|
if(k==="up"){mIdx=(mIdx+MENU.length-1)%MENU.length;paintMenu();}
|
||||||
|
else if(k==="down"){mIdx=(mIdx+1)%MENU.length;paintMenu();}
|
||||||
|
else if(k==="a"){go(MENU[mIdx].id);if(MENU[mIdx].id==="about"){dlgI=0;showLine(0);}}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(cur==="about"){
|
||||||
|
if(k==="a")advanceDlg();
|
||||||
|
else if(k==="b")go("menu");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(cur==="projects"){
|
||||||
|
const n=PROJECTS.slice(pg*PER,pg*PER+PER).length;
|
||||||
|
if(k==="up"){pSel=(pSel+n-1)%n;paintLv();}
|
||||||
|
else if(k==="down"){pSel=(pSel+1)%n;paintLv();}
|
||||||
|
else if(k==="left"){pg=(pg+pgCount-1)%pgCount;pSel=0;paintLv();}
|
||||||
|
else if(k==="right"){pg=(pg+1)%pgCount;pSel=0;paintLv();}
|
||||||
|
else if(k==="a")openProject();
|
||||||
|
else if(k==="b")go("menu");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(cur==="contact"){
|
||||||
|
if(k==="up"){ctIdx=(ctIdx+CONTACT.length-1)%CONTACT.length;paintCt();}
|
||||||
|
else if(k==="down"){ctIdx=(ctIdx+1)%CONTACT.length;paintCt();}
|
||||||
|
else if(k==="a")actContact();
|
||||||
|
else if(k==="b")go("menu");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(k==="b")go("menu");
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelectorAll("[data-k]").forEach(btn=>{
|
||||||
|
btn.addEventListener("click",()=>press(btn.dataset.k));
|
||||||
|
});
|
||||||
|
|
||||||
|
addEventListener("keydown",e=>{
|
||||||
|
const map={ArrowUp:"up",ArrowDown:"down",ArrowLeft:"left",ArrowRight:"right",
|
||||||
|
z:"a",Z:"a",Enter:"start",x:"b",X:"b",Escape:"b",Backspace:"b"};
|
||||||
|
const k=map[e.key];
|
||||||
|
if(k){e.preventDefault();press(k);}
|
||||||
|
});
|
||||||
|
|
||||||
|
function toggleSnd(){
|
||||||
|
sndOn=!sndOn;
|
||||||
|
document.querySelector("#scr-contact .pg-ind").textContent=sndOn?"♪ON":"♪OFF";
|
||||||
|
if(sndOn)blip(880);
|
||||||
|
}
|
||||||
|
document.querySelector('[data-k="select"]').addEventListener("click",toggleSnd);
|
||||||
|
|
||||||
|
fit();
|
||||||
|
if(RM){
|
||||||
|
go("title");
|
||||||
|
}else{
|
||||||
|
setTimeout(()=>{document.body.classList.add("booted");blip(1040,.09);},250);
|
||||||
|
setTimeout(()=>{if(cur==="boot")go("title");},2100);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,413 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="amber">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>miles_os — MILES WOLF ALLEN</title>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
:root{
|
||||||
|
--bg:#0a0705;--panel:#0f0b07;--ph:#ffb000;
|
||||||
|
--ph-dim:rgba(255,176,0,.55);--ph-faint:rgba(255,176,0,.16);--line:rgba(255,176,0,.28);
|
||||||
|
--mono:'IBM Plex Mono',ui-monospace,Menlo,monospace;
|
||||||
|
}
|
||||||
|
[data-theme="green"]{--bg:#040a05;--panel:#07110a;--ph:#3dff70;--ph-dim:rgba(61,255,112,.55);--ph-faint:rgba(61,255,112,.14);--line:rgba(61,255,112,.26)}
|
||||||
|
[data-theme="ice"]{--bg:#05070c;--panel:#0a0e15;--ph:#a8d8ff;--ph-dim:rgba(168,216,255,.55);--ph-faint:rgba(168,216,255,.13);--line:rgba(168,216,255,.24)}
|
||||||
|
*{margin:0;padding:0;box-sizing:border-box}
|
||||||
|
html,body{height:100%}
|
||||||
|
body{background:var(--bg);color:var(--ph);font-family:var(--mono);font-size:14px;line-height:1.55;overflow:hidden}
|
||||||
|
::selection{background:var(--ph);color:var(--bg)}
|
||||||
|
body::before{content:"";position:fixed;inset:0;z-index:60;pointer-events:none;
|
||||||
|
background:radial-gradient(120% 90% at 50% 45%,transparent 55%,rgba(0,0,0,.5) 100%)}
|
||||||
|
body::after{content:"";position:fixed;inset:0;z-index:61;pointer-events:none;opacity:.9;
|
||||||
|
background:repeating-linear-gradient(0deg,transparent 0 2px,rgba(0,0,0,.18) 2px 3px);
|
||||||
|
animation:flick 4s steps(1) infinite}
|
||||||
|
@keyframes flick{0%,100%{opacity:.9}31%{opacity:.82}32%{opacity:.9}67%{opacity:.85}68%{opacity:.9}}
|
||||||
|
a{color:var(--ph);text-decoration:underline dotted;text-underline-offset:3px}
|
||||||
|
a:hover{text-decoration-style:solid;text-shadow:0 0 8px var(--ph-dim)}
|
||||||
|
button{font-family:var(--mono)}
|
||||||
|
.app{height:100dvh;display:flex;flex-direction:column}
|
||||||
|
.bar{display:flex;align-items:center;gap:12px;padding:9px 14px;border-bottom:1px solid var(--line);flex:none}
|
||||||
|
.dots{display:flex;gap:6px}
|
||||||
|
.dots i{width:10px;height:10px;border-radius:50%;border:1px solid var(--ph-dim)}
|
||||||
|
.dots i:first-child{background:var(--ph)}
|
||||||
|
.bar-title{flex:1;text-align:center;color:var(--ph-dim);font-size:12px;letter-spacing:.08em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
||||||
|
.bar-btn{background:none;border:1px solid var(--line);color:var(--ph-dim);padding:3px 9px;font-size:11px;cursor:pointer;letter-spacing:.06em;transition:.15s}
|
||||||
|
.bar-btn:hover,.bar-btn[aria-pressed="true"]{color:var(--bg);background:var(--ph);border-color:var(--ph)}
|
||||||
|
#clock{color:var(--ph-dim);font-size:12px;font-variant-numeric:tabular-nums}
|
||||||
|
main{flex:1;display:grid;grid-template-columns:320px 1fr;min-height:0}
|
||||||
|
aside{border-right:1px solid var(--line);padding:20px;overflow-y:auto;display:flex;flex-direction:column;gap:22px;background:var(--panel)}
|
||||||
|
.banner pre{font-size:12.5px;line-height:1.4;color:var(--ph);text-shadow:0 0 10px var(--ph-dim);user-select:none}
|
||||||
|
.banner .sub{margin-top:10px;color:var(--ph-dim);font-size:11px;letter-spacing:.14em;text-transform:uppercase}
|
||||||
|
.sysinfo{border:1px solid var(--line);padding:14px;display:flex;flex-direction:column;gap:7px;font-size:12.5px}
|
||||||
|
.sysinfo div{display:flex;gap:10px}
|
||||||
|
.sysinfo dt{color:var(--ph-dim);width:74px;flex:none;text-transform:uppercase;font-size:11px;letter-spacing:.08em;padding-top:1px}
|
||||||
|
.sysinfo dd{color:var(--ph)}
|
||||||
|
.sysinfo .live{color:var(--ph);font-variant-numeric:tabular-nums}
|
||||||
|
.dot-on{display:inline-block;width:7px;height:7px;border-radius:50%;background:var(--ph);box-shadow:0 0 8px var(--ph);margin-right:6px;animation:pulse 2s infinite}
|
||||||
|
@keyframes pulse{50%{opacity:.35}}
|
||||||
|
.aside-note{color:var(--ph-dim);font-size:11px;line-height:1.7;border-left:2px solid var(--line);padding-left:12px}
|
||||||
|
#viewport{overflow-y:auto;padding:20px 24px;min-height:0;scrollbar-width:thin;scrollbar-color:var(--line) transparent}
|
||||||
|
#viewport::-webkit-scrollbar{width:8px}
|
||||||
|
#viewport::-webkit-scrollbar-thumb{background:var(--line)}
|
||||||
|
.ln{margin-bottom:7px;animation:lnIn .18s both;animation-delay:calc(var(--i,0)*26ms)}
|
||||||
|
@keyframes lnIn{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}
|
||||||
|
.ln.dim{color:var(--ph-dim)}
|
||||||
|
.ln.head{margin-top:16px;color:var(--ph);font-weight:600;letter-spacing:.1em;text-transform:uppercase;font-size:12px}
|
||||||
|
.ln.head::after{content:"";display:block;width:100%;height:1px;background:var(--line);margin-top:6px}
|
||||||
|
.echo{color:var(--ph-dim)}
|
||||||
|
.echo b{color:var(--ph);font-weight:600}
|
||||||
|
.proj{display:grid;grid-template-columns:30px 1fr auto;gap:12px;align-items:baseline;padding:8px 10px;margin-bottom:2px;border-left:2px solid var(--ph-faint);cursor:pointer;transition:.15s}
|
||||||
|
.proj:hover,.proj:focus-visible{background:var(--ph-faint);border-left-color:var(--ph);outline:none}
|
||||||
|
.proj .no{color:var(--ph-dim);font-size:11px}
|
||||||
|
.proj .t{font-weight:600}
|
||||||
|
.proj .d{grid-column:2/4;color:var(--ph-dim);font-size:12px;margin-top:-2px}
|
||||||
|
.proj .m{color:var(--ph-dim);font-size:11px;white-space:nowrap}
|
||||||
|
.proj .go{color:var(--ph);font-size:11px}
|
||||||
|
.meter{display:grid;grid-template-columns:118px 1fr 46px;gap:12px;align-items:center;margin-bottom:9px}
|
||||||
|
.meter .k{color:var(--ph);font-size:12.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
||||||
|
.track{height:10px;border:1px solid var(--line);position:relative;background:transparent}
|
||||||
|
.fill{position:absolute;top:1px;bottom:1px;left:1px;right:auto;width:calc(100% - 2px);background:var(--ph);opacity:.8;transform-origin:left;transform:scaleX(0);transition:transform .9s cubic-bezier(.2,.8,.2,1)}
|
||||||
|
.meter .v{color:var(--ph-dim);font-size:11px;text-align:right;font-variant-numeric:tabular-nums}
|
||||||
|
.kv{display:grid;grid-template-columns:150px 1fr;gap:4px 14px;font-size:13px}
|
||||||
|
.kv dt{color:var(--ph-dim);text-transform:uppercase;font-size:11px;letter-spacing:.08em;padding-top:2px}
|
||||||
|
.kv dd a{font-size:13px}
|
||||||
|
.stat-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:10px}
|
||||||
|
.stat-cell{border:1px solid var(--line);padding:12px}
|
||||||
|
.stat-cell b{display:block;font-size:22px;font-weight:600;letter-spacing:.02em}
|
||||||
|
.stat-cell span{color:var(--ph-dim);font-size:10.5px;text-transform:uppercase;letter-spacing:.1em}
|
||||||
|
.prompt-row{flex:none;display:flex;align-items:center;gap:10px;padding:12px 16px;border-top:1px solid var(--line);background:var(--panel)}
|
||||||
|
.ps1{color:var(--ph);font-weight:600;white-space:nowrap;font-size:13px}
|
||||||
|
.ps1 .path{color:var(--ph-dim);font-weight:400}
|
||||||
|
#cmd{flex:1;min-width:80px;background:none;border:none;outline:none;color:var(--ph);font:inherit;font-size:14px;caret-color:var(--ph)}
|
||||||
|
.chips{flex:none;display:none;gap:8px;padding:9px 14px;border-top:1px solid var(--line);overflow-x:auto;background:var(--panel);scrollbar-width:none}
|
||||||
|
.chips::-webkit-scrollbar{display:none}
|
||||||
|
.chips button{flex:none;background:none;border:1px solid var(--line);color:var(--ph-dim);padding:5px 12px;font-size:11.5px;cursor:pointer}
|
||||||
|
.chips button:active{background:var(--ph);color:var(--bg)}
|
||||||
|
#boot{position:fixed;inset:0;z-index:80;background:var(--bg);display:flex;flex-direction:column;justify-content:center;padding:10vh 8vw;gap:5px;transition:opacity .35s}
|
||||||
|
#boot.off{opacity:0;pointer-events:none}
|
||||||
|
.bl{opacity:0;color:var(--ph)}
|
||||||
|
.bl.on{opacity:1}
|
||||||
|
.bl.dim{color:var(--ph-dim)}
|
||||||
|
.boot-skip{position:absolute;right:8vw;bottom:8vh;color:var(--ph-dim);font-size:11px;letter-spacing:.1em}
|
||||||
|
.noscript-msg{padding:40px;text-align:center;color:var(--ph)}
|
||||||
|
@media (max-width:900px){
|
||||||
|
main{grid-template-columns:1fr}
|
||||||
|
aside{display:none}
|
||||||
|
.chips{display:flex}
|
||||||
|
body{font-size:13px}
|
||||||
|
}
|
||||||
|
@media (prefers-reduced-motion:reduce){
|
||||||
|
*{animation:none!important;transition:none!important}
|
||||||
|
.fill{transform:scaleX(var(--w,1))}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript><div class="noscript-msg">miles_os needs JavaScript to boot.<br>The human behind it: <a href="https://milesallen.site">Miles Wolf Allen</a> — [email protected]</div></noscript>
|
||||||
|
|
||||||
|
<div id="boot" aria-hidden="true">
|
||||||
|
<div class="bl">MILES_OS BIOS v3.0 — POST OK</div>
|
||||||
|
<div class="bl dim">cpu: human brain @ 3.2 thoughts/sec ............ OK</div>
|
||||||
|
<div class="bl dim">memory: 160 repos indexed ..................... OK</div>
|
||||||
|
<div class="bl dim">mounting /projects ............................ OK</div>
|
||||||
|
<div class="bl dim">mounting /games ............................... OK</div>
|
||||||
|
<div class="bl dim">starting phosphor driver [amber] .............. OK</div>
|
||||||
|
<div class="bl">welcome, guest.</div>
|
||||||
|
<span class="boot-skip">PRESS ANY KEY TO SKIP</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="app">
|
||||||
|
<header class="bar">
|
||||||
|
<div class="dots" aria-hidden="true"><i></i><i></i><i></i></div>
|
||||||
|
<div class="bar-title">[email protected]: ~ — miles_os v3.0</div>
|
||||||
|
<button class="bar-btn" id="sndBtn" aria-pressed="true">SND ON</button>
|
||||||
|
<button class="bar-btn" id="themeBtn">THEME</button>
|
||||||
|
<span id="clock">00:00:00</span>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<aside aria-label="system info">
|
||||||
|
<div class="banner">
|
||||||
|
<pre aria-hidden="true">
|
||||||
|
█▀▄▀█ ▀█▀ █░░ █▀▀ ▀█▀
|
||||||
|
█░▀░█ ░█░ █░░ █▀░ ░█░
|
||||||
|
▀░░░▀ ▀▀▀ ▀▀▀ ▀▀▀ ▀▀▀</pre>
|
||||||
|
<div class="sub">Miles Wolf Allen — Developer</div>
|
||||||
|
</div>
|
||||||
|
<dl class="sysinfo">
|
||||||
|
<div><dt>OS</dt><dd>miles_os 3.0 LTS</dd></div>
|
||||||
|
<div><dt>Host</dt><dd>school laptop</dd></div>
|
||||||
|
<div><dt>Shell</dt><dd>zsh + coffee</dd></div>
|
||||||
|
<div><dt>Editor</dt><dd>vscode</dd></div>
|
||||||
|
<div><dt>Uptime</dt><dd>since 2021</dd></div>
|
||||||
|
<div><dt>Session</dt><dd class="live" id="uptime">00:00:00</dd></div>
|
||||||
|
<div><dt>Status</dt><dd><span class="dot-on"></span>online</dd></div>
|
||||||
|
</dl>
|
||||||
|
<p class="aside-note">This whole site is a terminal. Type commands below, or click the menu buttons up top. Try `sudo hire-me`.</p>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<section id="viewport" role="log" aria-live="polite" aria-label="terminal output"></section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<div class="chips" aria-label="quick commands">
|
||||||
|
<button data-cmd="help">help</button>
|
||||||
|
<button data-cmd="about">about</button>
|
||||||
|
<button data-cmd="skills">skills</button>
|
||||||
|
<button data-cmd="projects">projects</button>
|
||||||
|
<button data-cmd="now">now</button>
|
||||||
|
<button data-cmd="stats">stats</button>
|
||||||
|
<button data-cmd="contact">contact</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="prompt-row">
|
||||||
|
<label class="ps1" for="cmd">guest@miles_os<span class="path">:~$</span></label>
|
||||||
|
<input id="cmd" type="text" autocomplete="off" autocapitalize="off" spellcheck="false" aria-label="type a command">
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
"use strict";
|
||||||
|
const RM=matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||||||
|
const $=s=>document.querySelector(s);
|
||||||
|
const vp=$("#viewport"),input=$("#cmd"),boot=$("#boot");
|
||||||
|
let sndOn=true,audioCtx=null,lineIdx=0;
|
||||||
|
const hist=[];let histIdx=-1;
|
||||||
|
|
||||||
|
function blip(f,d){
|
||||||
|
if(!sndOn)return;
|
||||||
|
try{
|
||||||
|
audioCtx=audioCtx||new (window.AudioContext||window.webkitAudioContext)();
|
||||||
|
if(audioCtx.state==="suspended")audioCtx.resume();
|
||||||
|
const o=audioCtx.createOscillator(),g=audioCtx.createGain();
|
||||||
|
o.type="square";o.frequency.value=f||1040;
|
||||||
|
g.gain.setValueAtTime(.028,audioCtx.currentTime);
|
||||||
|
g.gain.exponentialRampToValueAtTime(.0001,audioCtx.currentTime+(d||.045));
|
||||||
|
o.connect(g).connect(audioCtx.destination);
|
||||||
|
o.start();o.stop(audioCtx.currentTime+(d||.045)+.02);
|
||||||
|
}catch(e){}
|
||||||
|
}
|
||||||
|
|
||||||
|
function print(html,cls){
|
||||||
|
const d=document.createElement("div");
|
||||||
|
d.className="ln"+(cls?" "+cls:"");
|
||||||
|
d.style.setProperty("--i",Math.min(lineIdx++,30));
|
||||||
|
d.innerHTML=html;
|
||||||
|
vp.appendChild(d);
|
||||||
|
vp.scrollTop=vp.scrollHeight;
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
function gap(){lineIdx=0;}
|
||||||
|
|
||||||
|
const PROJECTS=[
|
||||||
|
{n:"01",t:"OmniEmu 2.0",d:"All your emulators in one place.",m:"ACTIVE · TS",u:"https://milesallen.site/projects/omniemu.html"},
|
||||||
|
{n:"02",t:"Vanilla",d:"A browser built to be rebuilt. The main quest.",m:"MAIN · TS",u:"https://vanilla.milesallen.site"},
|
||||||
|
{n:"03",t:"Gelectron",d:"Electron internals, documented and dissected.",m:"EARLY · TS",u:"https://milesallen.site/projects/gelectron.html"},
|
||||||
|
{n:"04",t:"FEDL",d:"Geometry Dash level list. ~4,000 verified runs.",m:"LIVE · SITE",u:"https://me.fedl.site"},
|
||||||
|
{n:"05",t:"Moon Gaming",d:"Community hub. 1,542 commits and counting.",m:"COMMUNITY · JS",u:"https://github.com/mileswolfallen2"},
|
||||||
|
{n:"06",t:"Noto",d:"Notes app experiment. On the back burner.",m:"PAUSED · TS",u:"https://milesallen.site/projects/noto.html"},
|
||||||
|
{n:"07",t:"Portfolio v3",d:"The site you came from. Street-poster energy.",m:"LIVE · HTML",u:"https://milesallen.site"},
|
||||||
|
{n:"08",t:"Redesign Lab",d:"Nine+ design languages, one portfolio.",m:"LAB · HTML",u:"https://milesallen.site/redesign/hub.html"},
|
||||||
|
{n:"09",t:"Emulator Core",d:"20+ cores wrapped in one launcher.",m:"ACTIVE · TS",u:"https://milesallen.site/projects/omniemu.html"},
|
||||||
|
{n:"10",t:"GD Tools",d:"Utilities for the demon list grind.",m:"SIDE · JS",u:"https://me.fedl.site"},
|
||||||
|
{n:"11",t:"School Utils",d:"Tools that make the school day shorter.",m:"SIDE · PY",u:"https://github.com/mileswolfallen2"},
|
||||||
|
{n:"12",t:"Pixel Playground",d:"Canvas experiments and shader doodles.",m:"WIP · JS",u:"https://codepen.io/mileswolfallen2-the-typescripter"},
|
||||||
|
{n:"13",t:"This Terminal",d:"You are inside the project right now.",m:"META · VANILLA JS",u:"#"}
|
||||||
|
];
|
||||||
|
const SKILLS=[
|
||||||
|
["JavaScript",90],["HTML / CSS",92],["TypeScript",82],["Python",75],
|
||||||
|
["Swift",65],["Git",86],["Node",78],["Blender",70],
|
||||||
|
["Electron internals",62],["Rust",38]
|
||||||
|
];
|
||||||
|
const STATS=[["160+","public repos"],["91","stars earned"],["3,800+","FEDL runs verified"],["20+","emulator cores"],["1,542","Moon Gaming commits"],["13","shipped builds"]];
|
||||||
|
const CONTACT=[
|
||||||
|
["email","[email protected]","mailto:[email protected]"],
|
||||||
|
["github","@mileswolfallen2","https://github.com/mileswolfallen2"],
|
||||||
|
["replit","@MilesAllen1","https://replit.com/@MilesAllen1"],
|
||||||
|
["codepen","mileswolfallen2-the-typescripter","https://codepen.io/mileswolfallen2-the-typescripter"],
|
||||||
|
["fedl","me.fedl.site","https://me.fedl.site"]
|
||||||
|
];
|
||||||
|
const NOW=[
|
||||||
|
["Vanilla","the main quest — a browser rebuilt from scratch, active daily"],
|
||||||
|
["Gelectron","side quest — Electron internals, BrowserWindow / IPC / Menu / Tray"],
|
||||||
|
["FEDL","live ops — ~4,000 runs verified across 340+ ranked levels"],
|
||||||
|
["Noto","back burner — notes app, waiting for its moment"]
|
||||||
|
];
|
||||||
|
const ABOUT=[
|
||||||
|
"Hey — I'm Miles Wolf Allen. I'm a student developer who ships.",
|
||||||
|
"I build browsers (Vanilla), tear down Electron (Gelectron), run a Geometry Dash level list (FEDL),",
|
||||||
|
"and maintain way too many side projects. If it renders on a screen, I'm interested.",
|
||||||
|
"Currently: Vanilla full-time, everything else in the background."
|
||||||
|
];
|
||||||
|
|
||||||
|
function secHead(t){print(t,"head");}
|
||||||
|
function renderProjects(){
|
||||||
|
gap();secHead("/projects — 13 entries");
|
||||||
|
print("<span class='dim'>click a row or type `open <n>`</span>","dim");
|
||||||
|
PROJECTS.forEach(p=>{
|
||||||
|
const row=print(`<span class="no">${p.n}</span><span class="t">${p.t}</span><span class="m">${p.m} <span class="go">[open ↗]</span></span><span class="d">${p.d}</span>`,"proj");
|
||||||
|
row.tabIndex=0;row.dataset.url=p.u;
|
||||||
|
const go=()=>{if(p.u==="#"){exec("about");}else{window.open(p.u,"_blank","noopener");}};
|
||||||
|
row.addEventListener("click",()=>{blip(1240);go();});
|
||||||
|
row.addEventListener("keydown",e=>{if(e.key==="Enter")go();});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function renderSkills(){
|
||||||
|
gap();secHead("/skills — proficiency meters");
|
||||||
|
SKILLS.forEach(([k,v])=>{
|
||||||
|
const m=print(`<span class="k">${k}</span><span class="track"><span class="fill"></span></span><span class="v">${v}%</span>`,"meter");
|
||||||
|
const f=m.querySelector(".fill");
|
||||||
|
requestAnimationFrame(()=>requestAnimationFrame(()=>{f.style.transform=`scaleX(${v/100})`;}));
|
||||||
|
});
|
||||||
|
print("<span class='dim'>queued packages: php · dotnet · nix · yarn — install in progress</span>","dim");
|
||||||
|
}
|
||||||
|
function renderStats(){
|
||||||
|
gap();secHead("/stats.log");
|
||||||
|
let g="";
|
||||||
|
STATS.forEach(([v,k])=>{g+=`<div class="stat-cell"><b>${v}</b><span>${k}</span></div>`;});
|
||||||
|
print(`<div class="stat-grid">${g}</div>`);
|
||||||
|
}
|
||||||
|
function renderContact(){
|
||||||
|
gap();secHead("/contact.cfg");
|
||||||
|
CONTACT.forEach(([k,v,u])=>{
|
||||||
|
print(`<span class="dim">${k.padEnd(9," ")}</span><a href="${u}" target="_blank" rel="noopener">${v}</a>`);
|
||||||
|
});
|
||||||
|
print("<span class='dim'>response time: usually same day. no bots please.</span>","dim");
|
||||||
|
}
|
||||||
|
function renderNow(){
|
||||||
|
gap();secHead("~/now.md");
|
||||||
|
NOW.forEach(([t,d])=>print(`<b>${t}</b> — <span class="dim">${d}</span>`));
|
||||||
|
print("<span class='dim'>last updated: aug 2026</span>","dim");
|
||||||
|
}
|
||||||
|
function renderAbout(){
|
||||||
|
gap();secHead("~/about.txt");
|
||||||
|
ABOUT.forEach(l=>print(l));
|
||||||
|
}
|
||||||
|
function renderHelp(){
|
||||||
|
gap();secHead("available commands");
|
||||||
|
[
|
||||||
|
["about","who is miles"],["skills","proficiency meters"],["projects","list all 13 builds"],
|
||||||
|
["open <n>","launch project n"],["now","current focus"],["stats","github numbers"],
|
||||||
|
["contact","reach out"],["theme [name]","amber · green · ice"],["clear","wipe the screen"],
|
||||||
|
["ls","list files"],["sudo hire-me","??? "],["exit","good luck"]
|
||||||
|
].forEach(([c,d])=>print(`<b>${c}</b><span class="dim"> — ${d}</span>`));
|
||||||
|
}
|
||||||
|
|
||||||
|
function exec(raw){
|
||||||
|
const cmd=raw.trim();
|
||||||
|
print(`<span class="echo"><b>guest@miles_os:~$</b> ${cmd.replace(/</g,"<")}</span>`);
|
||||||
|
if(!cmd){return;}
|
||||||
|
hist.push(cmd);histIdx=hist.length;
|
||||||
|
blip(1040);
|
||||||
|
const [c,...rest]=cmd.split(/\s+/);
|
||||||
|
const arg=rest.join(" ");
|
||||||
|
switch(c.toLowerCase()){
|
||||||
|
case "help":renderHelp();break;
|
||||||
|
case "about":case "cat about.txt":renderAbout();break;
|
||||||
|
case "skills":case "ls skills":renderSkills();break;
|
||||||
|
case "projects":case "ls projects":renderProjects();break;
|
||||||
|
case "open":{
|
||||||
|
const p=PROJECTS.find(x=>x.n==="0"+arg||x.n===arg||(arg&&x.t.toLowerCase().includes(arg.toLowerCase())));
|
||||||
|
if(p){print(`opening ${p.t} …`,"dim");if(p.u==="#"){exec("about");}else{window.open(p.u,"_blank","noopener");}}
|
||||||
|
else print(`open: no such project '${arg.replace(/</g,"<")}' — try \`projects\``,"dim");
|
||||||
|
break;}
|
||||||
|
case "now":renderNow();break;
|
||||||
|
case "stats":renderStats();break;
|
||||||
|
case "contact":renderContact();break;
|
||||||
|
case "theme":{
|
||||||
|
const t=(arg||"").toLowerCase();
|
||||||
|
const order=["amber","green","ice"];
|
||||||
|
const next=t&&order.includes(t)?t:order[(order.indexOf(document.documentElement.dataset.theme)+1)%order.length];
|
||||||
|
document.documentElement.dataset.theme=next;
|
||||||
|
print(`phosphor driver → [${next}]`,"dim");blip(660);
|
||||||
|
break;}
|
||||||
|
case "clear":vp.innerHTML="";gap();break;
|
||||||
|
case "ls":print("about.txt skills/ projects/ now.md stats.log contact.cfg","dim");break;
|
||||||
|
case "cat":print(`cat: ${arg?arg.replace(/</g,"<"):""}: try \`about\`, \`skills\`, \`projects\`, \`now\`, \`stats\`, \`contact\``,"dim");break;
|
||||||
|
case "whoami":print("guest — but you can be miles if you ship enough.","dim");break;
|
||||||
|
case "date":print(new Date().toString(),"dim");break;
|
||||||
|
case "echo":print(arg?arg.replace(/</g,"<"):"","dim");break;
|
||||||
|
case "man":print("tldr: just type the command. it's not that kind of manual.","dim");break;
|
||||||
|
case "pwd":print("/home/guest/miles","dim");break;
|
||||||
|
case "sudo":
|
||||||
|
if(arg.toLowerCase().replace(/\s+/g,"-")==="hire-me"){
|
||||||
|
print("[sudo] password for guest: ********","dim");
|
||||||
|
print("access granted. drafting offer letter…");
|
||||||
|
print("just kidding — but seriously: <a href='mailto:[email protected]'>[email protected]</a>");
|
||||||
|
} else if(/rm\s+-rf\s+\//.test(arg)){
|
||||||
|
print("nice try. this incident will be reported.","dim");
|
||||||
|
} else {
|
||||||
|
print("sudo: only 'hire-me' is allowed here.","dim");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "rm":print("permission denied: this portfolio is load-bearing.","dim");break;
|
||||||
|
case "exit":print("there is no escape. close the tab manually.","dim");break;
|
||||||
|
case "konami":print("↑↑↓↓←→←→BA — nothing happened. wrong console.","dim");break;
|
||||||
|
default:print(`command not found: ${cmd.split(/\s+/)[0].replace(/</g,"<")} — type \`help\``,"dim");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input.addEventListener("keydown",e=>{
|
||||||
|
if(e.key==="Enter"){exec(input.value);input.value="";}
|
||||||
|
else if(e.key==="ArrowUp"){e.preventDefault();if(histIdx>0){histIdx--;input.value=hist[histIdx]||"";}}
|
||||||
|
else if(e.key==="ArrowDown"){e.preventDefault();if(histIdx<hist.length){histIdx++;input.value=hist[histIdx]||"";}}
|
||||||
|
else blip(1320,.02);
|
||||||
|
});
|
||||||
|
document.querySelector(".app").addEventListener("click",e=>{
|
||||||
|
if(e.target.closest("a,button,input"))return;
|
||||||
|
if(String(getSelection()))return;
|
||||||
|
input.focus({preventScroll:true});
|
||||||
|
});
|
||||||
|
document.querySelectorAll(".chips button").forEach(b=>b.addEventListener("click",()=>{
|
||||||
|
exec(b.dataset.cmd);input.focus({preventScroll:true});
|
||||||
|
}));
|
||||||
|
$("#themeBtn").addEventListener("click",()=>exec("theme"));
|
||||||
|
$("#sndBtn").addEventListener("click",function(){
|
||||||
|
sndOn=!sndOn;
|
||||||
|
this.textContent=sndOn?"SND ON":"SND OFF";
|
||||||
|
this.setAttribute("aria-pressed",String(sndOn));
|
||||||
|
if(sndOn)blip(880);
|
||||||
|
});
|
||||||
|
|
||||||
|
setInterval(()=>{
|
||||||
|
$("#clock").textContent=new Date().toLocaleTimeString("en-GB");
|
||||||
|
},1000);
|
||||||
|
$("#clock").textContent=new Date().toLocaleTimeString("en-GB");
|
||||||
|
const t0=Date.now();
|
||||||
|
setInterval(()=>{
|
||||||
|
const s=Math.floor((Date.now()-t0)/1000);
|
||||||
|
const hh=String(Math.floor(s/3600)).padStart(2,"0");
|
||||||
|
const mm=String(Math.floor(s%3600/60)).padStart(2,"0");
|
||||||
|
const ss=String(s%60).padStart(2,"0");
|
||||||
|
$("#uptime").textContent=`${hh}:${mm}:${ss}`;
|
||||||
|
},1000);
|
||||||
|
|
||||||
|
const bootLines=[...boot.querySelectorAll(".bl")];
|
||||||
|
let bi=0,done=false;
|
||||||
|
function endBoot(){
|
||||||
|
if(done)return;done=true;
|
||||||
|
boot.classList.add("off");
|
||||||
|
setTimeout(()=>boot.remove(),400);
|
||||||
|
gap();
|
||||||
|
print("welcome, guest. you are inside <b>miles_os</b> — a portfolio disguised as a terminal.");
|
||||||
|
print("type <b>help</b> to list commands, or click a menu button above.","dim");
|
||||||
|
input.focus({preventScroll:true});
|
||||||
|
}
|
||||||
|
if(RM){endBoot();}
|
||||||
|
else{
|
||||||
|
const iv=setInterval(()=>{
|
||||||
|
if(bi>=bootLines.length){clearInterval(iv);setTimeout(endBoot,350);return;}
|
||||||
|
bootLines[bi++].classList.add("on");
|
||||||
|
blip(520+bio(),.03);
|
||||||
|
},130);
|
||||||
|
function bio(){return bi*90;}
|
||||||
|
addEventListener("keydown",endBoot,{once:true});
|
||||||
|
boot.addEventListener("click",endBoot,{once:true});
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,843 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>MILES WOLF ALLEN — SOFT ⇄ LOUD</title>
|
||||||
|
<meta name="description" content="Redesign expression three — modern simplistic maximalism. The page fluctuates between Swiss-quiet and neon-loud as you scroll.">
|
||||||
|
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Crect width='100' height='100' fill='%23FF2EA6'/%3E%3Ctext x='50' y='74' font-size='64' font-family='Arial Black' font-weight='900' text-anchor='middle' fill='%23000'%3EM%3C/text%3E%3C/svg%3E">
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Anton&family=Instrument+Serif:ital@0;1&family=Space+Grotesk:wght@400;500;700&family=Space+Mono:ital,wght@0,400;0,700;1,400&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||||||
|
<style>
|
||||||
|
*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
|
||||||
|
:root{
|
||||||
|
--paper:#F4F1EA;
|
||||||
|
--ink:#131311;
|
||||||
|
--mut:#7d7a72;
|
||||||
|
--line:rgba(19,19,17,.16);
|
||||||
|
--yellow:#F7FF00;
|
||||||
|
--pink:#FF2EA6;
|
||||||
|
--cyan:#00F4FF;
|
||||||
|
--orange:#FF5C00;
|
||||||
|
--green:#00FF85;
|
||||||
|
--purple:#8A2BFF;
|
||||||
|
--grot:'Space Grotesk',sans-serif;
|
||||||
|
--mono:'Space Mono',monospace;
|
||||||
|
--disp:'Anton',Impact,sans-serif;
|
||||||
|
--serif:'Instrument Serif',serif;
|
||||||
|
--ease:cubic-bezier(.16,1,.3,1);
|
||||||
|
}
|
||||||
|
html{scroll-behavior:smooth;overflow-x:hidden;scrollbar-width:thin;scrollbar-color:var(--ink) var(--paper)}
|
||||||
|
body{
|
||||||
|
background:var(--paper);color:var(--ink);
|
||||||
|
font-family:var(--grot);font-size:16px;line-height:1.6;
|
||||||
|
overflow-x:hidden;-webkit-font-smoothing:antialiased;
|
||||||
|
transition:background .5s ease,color .5s ease;
|
||||||
|
}
|
||||||
|
::selection{background:var(--pink);color:#000}
|
||||||
|
a{color:inherit;text-decoration:none}
|
||||||
|
button{font:inherit;color:inherit;background:none;border:none;cursor:pointer}
|
||||||
|
img,svg{display:block}
|
||||||
|
|
||||||
|
.spark{position:fixed;width:6px;height:6px;border-radius:50%;pointer-events:none;z-index:1200;margin:-3px}
|
||||||
|
|
||||||
|
.loader{position:fixed;inset:0;z-index:1100;background:var(--paper);color:var(--ink);
|
||||||
|
display:flex;flex-direction:column;justify-content:space-between;padding:28px 5vw}
|
||||||
|
.loader-top{display:flex;justify-content:space-between;font-family:var(--mono);font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--mut)}
|
||||||
|
.loader-mid{align-self:center;font-family:var(--disp);font-size:clamp(60px,14vw,180px);line-height:1;text-transform:uppercase;letter-spacing:.01em}
|
||||||
|
.loader-mid.is-loud{color:transparent;-webkit-text-stroke:2px var(--pink)}
|
||||||
|
.loader-bot{display:flex;align-items:flex-end;justify-content:space-between;gap:24px}
|
||||||
|
.loader-count{font-family:var(--mono);font-weight:700;font-size:clamp(28px,5vw,54px);line-height:1}
|
||||||
|
.loader-count small{font-size:.4em;color:var(--mut);font-weight:400}
|
||||||
|
.loader-bar{flex:1;height:2px;background:var(--line);position:relative;overflow:hidden;margin-bottom:10px}
|
||||||
|
.loader-bar i{position:absolute;inset:0;background:var(--pink);transform-origin:left;transform:scaleX(0)}
|
||||||
|
|
||||||
|
.nav{position:sticky;top:0;z-index:800;display:flex;align-items:center;justify-content:space-between;gap:18px;
|
||||||
|
padding:14px 5vw;background:color-mix(in srgb,var(--paper) 88%,transparent);
|
||||||
|
backdrop-filter:blur(10px);border-bottom:1px solid var(--line)}
|
||||||
|
.brand{font-family:var(--mono);font-weight:700;font-size:13px;letter-spacing:.06em}
|
||||||
|
.brand span{color:var(--mut);font-weight:400}
|
||||||
|
.nav-links{display:flex;gap:26px}
|
||||||
|
.nav-links a{font-family:var(--mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--mut);transition:color .25s}
|
||||||
|
.nav-links a:hover{color:var(--pink)}
|
||||||
|
.switch{display:flex;align-items:center;gap:10px;cursor:pointer;user-select:none}
|
||||||
|
.switch input{position:absolute;opacity:0;pointer-events:none}
|
||||||
|
.sw-track{width:44px;height:22px;border:1px solid var(--ink);border-radius:99px;position:relative;transition:background .3s,border-color .3s}
|
||||||
|
.sw-thumb{position:absolute;top:2px;left:2px;width:16px;height:16px;border-radius:50%;background:var(--ink);transition:transform .35s var(--ease),background .3s}
|
||||||
|
.switch input:checked + .sw-track{background:var(--pink);border-color:var(--pink)}
|
||||||
|
.switch input:checked + .sw-track .sw-thumb{transform:translateX(22px);background:#000}
|
||||||
|
.sw-txt{font-family:var(--mono);font-size:10px;letter-spacing:.16em;color:var(--mut);min-width:38px}
|
||||||
|
.sw-txt.on{color:var(--pink)}
|
||||||
|
|
||||||
|
.vu{position:fixed;right:26px;top:50%;transform:translateY(-50%);z-index:700;
|
||||||
|
display:flex;flex-direction:column;align-items:center;gap:12px;--vu-c:var(--ink)}
|
||||||
|
.vu-cap{font-family:var(--mono);font-size:9px;letter-spacing:.1em;color:var(--mut)}
|
||||||
|
.vu-track{width:3px;height:32vh;background:var(--line);position:relative;border-radius:2px}
|
||||||
|
.vu-fill{position:absolute;bottom:0;left:0;width:100%;height:0;background:var(--vu-c);border-radius:2px;transition:background .4s}
|
||||||
|
.vu-needle{position:absolute;left:50%;top:0;width:16px;height:2px;background:var(--vu-c);transform:translate(-50%,-50%);transition:background .4s}
|
||||||
|
.vu-read{writing-mode:vertical-rl;font-family:var(--mono);font-size:9px;letter-spacing:.3em;color:var(--vu-c);transition:color .4s}
|
||||||
|
|
||||||
|
section{position:relative}
|
||||||
|
.q{background:var(--paper);color:var(--ink);padding:clamp(90px,11vw,150px) 5vw;transition:background .5s ease,color .5s ease}
|
||||||
|
.l{color:#000;padding:clamp(80px,10vw,130px) 5vw;overflow:hidden}
|
||||||
|
|
||||||
|
.q-head{display:flex;align-items:baseline;gap:22px;border-top:1px solid var(--line);padding-top:20px;margin-bottom:clamp(44px,6vw,84px)}
|
||||||
|
.q-no{font-family:var(--mono);font-size:11px;color:var(--mut)}
|
||||||
|
.q-title{font-family:var(--grot);font-weight:500;font-size:clamp(30px,4.6vw,62px);letter-spacing:-.02em;line-height:1}
|
||||||
|
.q-tag{margin-left:auto;font-family:var(--mono);font-size:10px;letter-spacing:.18em;color:var(--mut);white-space:nowrap}
|
||||||
|
.l-head{display:flex;align-items:flex-start;gap:22px;margin-bottom:clamp(40px,5vw,70px);flex-wrap:wrap}
|
||||||
|
.l-no{font-family:var(--mono);font-size:11px;background:#000;color:#fff;padding:6px 10px;transform:rotate(-2deg)}
|
||||||
|
.l-title{font-family:var(--disp);font-size:clamp(52px,10vw,140px);line-height:.92;text-transform:uppercase;letter-spacing:.01em}
|
||||||
|
.l-tag{margin-left:auto;font-family:var(--mono);font-size:10px;letter-spacing:.18em;background:#000;color:#fff;padding:8px 12px;transform:rotate(1.5deg);white-space:nowrap}
|
||||||
|
|
||||||
|
.hero{min-height:100svh;display:flex;flex-direction:column;padding-top:clamp(40px,8vh,90px);padding-bottom:0}
|
||||||
|
.hero-eyebrow{display:flex;justify-content:space-between;gap:16px;font-family:var(--mono);font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:var(--mut);
|
||||||
|
border-bottom:1px solid var(--line);padding-bottom:14px;flex-wrap:wrap}
|
||||||
|
.hero-name{font-family:var(--disp);text-transform:uppercase;line-height:.86;letter-spacing:.005em;
|
||||||
|
font-size:clamp(66px,15.5vw,230px);position:relative;z-index:2;margin-block:auto;padding-block:clamp(24px,4vh,48px)}
|
||||||
|
.hero-name .ln{display:block}
|
||||||
|
.hero-name .outline{color:transparent;-webkit-text-stroke:2.5px var(--ink);transition:-webkit-text-stroke .5s}
|
||||||
|
.hero-star{display:inline-block;color:var(--pink);font-size:.42em;vertical-align:.62em;margin-left:.08em;animation:spin 7s linear infinite}
|
||||||
|
@keyframes spin{to{rotate:360deg}}
|
||||||
|
.chw{display:inline-block;overflow:hidden;vertical-align:top;padding-bottom:.06em;margin-bottom:-.06em}
|
||||||
|
.ch{display:inline-block;will-change:transform}
|
||||||
|
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.25}}
|
||||||
|
.hero-low{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,.85fr);gap:clamp(28px,4vw,60px);align-items:start;
|
||||||
|
border-top:1px solid var(--line);padding:26px 0 40px}
|
||||||
|
.hero-state{font-size:clamp(19px,2.3vw,30px);line-height:1.35;font-weight:500;letter-spacing:-.01em;max-width:24ch}
|
||||||
|
.hero-state em{font-family:var(--serif);font-style:italic;font-weight:400;font-size:1.12em}
|
||||||
|
.side-status{display:inline-flex;align-items:center;gap:9px;font-family:var(--mono);font-weight:700;font-size:11px;letter-spacing:.1em;text-transform:uppercase;
|
||||||
|
border:1px solid var(--ink);border-radius:99px;padding:10px 16px;margin-bottom:14px}
|
||||||
|
.side-status .dot{width:8px;height:8px;border-radius:50%;background:var(--green);animation:pulse 1.6s ease-in-out infinite}
|
||||||
|
.side-row{display:flex;justify-content:space-between;gap:14px;font-family:var(--mono);font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--mut);padding:11px 0;border-bottom:1px solid var(--line)}
|
||||||
|
.side-row:last-child{border-bottom:none}
|
||||||
|
.side-row b{color:var(--ink);font-weight:700}
|
||||||
|
|
||||||
|
.band{background:#000;color:var(--yellow);overflow:hidden;transform:rotate(-1.2deg) scale(1.02);border-block:3px solid #000;position:relative;z-index:5}
|
||||||
|
.marquee{display:flex;width:max-content;animation:mq 26s linear infinite}
|
||||||
|
.band.rev .marquee{animation-direction:reverse}
|
||||||
|
.band:hover .marquee{animation-play-state:paused}
|
||||||
|
.mq-set{display:flex;align-items:center;gap:38px;padding-right:38px;
|
||||||
|
font-family:var(--disp);text-transform:uppercase;font-size:clamp(26px,4.6vw,58px);line-height:1;padding-block:16px;white-space:nowrap}
|
||||||
|
.mq-set .ast{color:var(--pink);animation:spin 5s linear infinite}
|
||||||
|
.mq-set .ghost{color:transparent;-webkit-text-stroke:1.5px var(--yellow)}
|
||||||
|
@keyframes mq{to{transform:translateX(-50%)}}
|
||||||
|
|
||||||
|
.about-grid{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,.85fr);gap:clamp(40px,6vw,90px)}
|
||||||
|
.about-state{font-size:clamp(26px,3.6vw,50px);line-height:1.22;font-weight:500;letter-spacing:-.02em;margin-bottom:34px}
|
||||||
|
.about-state em{font-family:var(--serif);font-style:italic;font-weight:400;color:var(--pink);font-size:1.08em;transition:color .5s}
|
||||||
|
body.loud-mode .about-state em{color:var(--cyan)}
|
||||||
|
.about-copy p{max-width:56ch;color:var(--mut);margin-bottom:18px;font-size:15px}
|
||||||
|
.about-copy p b{color:var(--ink);font-weight:600}
|
||||||
|
.about-links{display:flex;flex-wrap:wrap;gap:12px;margin-top:30px}
|
||||||
|
.pill{font-family:var(--mono);font-size:11px;letter-spacing:.1em;text-transform:uppercase;border:1px solid var(--ink);
|
||||||
|
padding:11px 18px;border-radius:99px;transition:all .3s var(--ease)}
|
||||||
|
.pill:hover{background:var(--ink);color:var(--paper);transform:translateY(-2px)}
|
||||||
|
.facts{border-top:1px solid var(--line)}
|
||||||
|
.fact{display:grid;grid-template-columns:110px 1fr;gap:16px;padding:16px 0;border-bottom:1px solid var(--line);align-items:baseline}
|
||||||
|
.fact dt{font-family:var(--mono);font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:var(--mut)}
|
||||||
|
.fact dd{font-size:15px;font-weight:500}
|
||||||
|
.fact dd .live{display:inline-block;width:8px;height:8px;border-radius:50%;background:var(--green);margin-right:8px;animation:pulse 1.6s infinite}
|
||||||
|
.badge-chip{display:inline-block;font-family:var(--mono);font-size:10px;border:1px solid var(--line);padding:4px 9px;margin:2px 4px 2px 0;border-radius:99px}
|
||||||
|
|
||||||
|
.loadout{background:var(--pink)}
|
||||||
|
.loadout .l-title{text-shadow:5px 5px 0 #000}
|
||||||
|
.loadout .l-no,.loadout .l-tag{background:#000}
|
||||||
|
.chip-group{display:flex;align-items:baseline;gap:14px;margin:0 0 16px;font-family:var(--mono);font-size:10px;letter-spacing:.18em;text-transform:uppercase}
|
||||||
|
.chip-group:first-child{margin-top:0}
|
||||||
|
.chip-group + .chip-wall{margin-bottom:38px}
|
||||||
|
.chip-group i{flex:1;height:1px;background:rgba(0,0,0,.35)}
|
||||||
|
.chip-wall{display:flex;flex-wrap:wrap;gap:14px}
|
||||||
|
.chip{--r:0deg;font-family:var(--mono);font-weight:700;font-size:clamp(13px,1.5vw,17px);letter-spacing:.04em;text-transform:uppercase;
|
||||||
|
background:#fff;color:#000;border:2px solid #000;padding:14px 22px;rotate:var(--r);
|
||||||
|
box-shadow:4px 4px 0 #000;cursor:default;transition:box-shadow .2s,translate .2s;will-change:transform}
|
||||||
|
.chip:hover{animation:jello 1s;translate:-2px -2px;box-shadow:8px 8px 0 #000}
|
||||||
|
.chip.locked{background:transparent;color:#fff;border-style:dashed;box-shadow:none}
|
||||||
|
.chip.locked:hover{animation:rubberBand 1s}
|
||||||
|
|
||||||
|
.builds .rows{border-top:1px solid var(--line)}
|
||||||
|
.row{position:relative;display:grid;grid-template-columns:64px minmax(0,1fr) auto 44px;gap:20px;align-items:center;
|
||||||
|
padding:26px 10px;border-bottom:1px solid var(--line);overflow:hidden;isolation:isolate}
|
||||||
|
.row::before{content:'';position:absolute;inset:0;background:var(--ink);transform:scaleY(0);transform-origin:bottom;
|
||||||
|
transition:transform .45s var(--ease);z-index:-1}
|
||||||
|
.row::after{content:'';position:absolute;top:0;bottom:0;left:-80%;width:45%;
|
||||||
|
background:linear-gradient(105deg,transparent,rgba(255,255,255,.28),transparent);transform:skewX(-18deg);transition:left .7s var(--ease)}
|
||||||
|
.row:hover::before{transform:scaleY(1)}
|
||||||
|
.row:hover::after{left:130%}
|
||||||
|
.row-no{font-family:var(--mono);font-size:11px;color:var(--mut);transition:color .3s}
|
||||||
|
.row-title{display:block;font-weight:700;font-size:clamp(21px,2.8vw,36px);letter-spacing:-.02em;line-height:1.1;transition:transform .45s var(--ease),color .3s}
|
||||||
|
.row-desc{display:block;color:var(--mut);font-size:13px;margin-top:5px;max-width:52ch;transition:color .3s}
|
||||||
|
.row-meta{font-family:var(--mono);font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--mut);white-space:nowrap;transition:color .3s}
|
||||||
|
.row-arrow{font-size:20px;justify-self:end;transition:transform .45s var(--ease),color .3s}
|
||||||
|
.row:hover .row-title{color:var(--paper);transform:translateX(10px)}
|
||||||
|
.row:hover .row-desc,.row:hover .row-meta,.row:hover .row-no{color:rgba(244,241,234,.65)}
|
||||||
|
.row:hover .row-arrow{color:var(--yellow);transform:translate(4px,-4px)}
|
||||||
|
|
||||||
|
.now{background:var(--yellow);color:#000}
|
||||||
|
.now .l-no{background:#000;color:var(--yellow)}
|
||||||
|
.now .l-tag{background:#000;color:var(--yellow)}
|
||||||
|
.focus-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:18px}
|
||||||
|
.focus{display:flex;flex-direction:column;gap:14px;border:2px solid #000;padding:26px 24px 30px;
|
||||||
|
box-shadow:6px 6px 0 #000;transition:transform .35s var(--ease),box-shadow .35s var(--ease)}
|
||||||
|
.focus:hover{transform:translate(-4px,-4px);box-shadow:12px 12px 0 #000}
|
||||||
|
.focus-no{font-family:var(--mono);font-size:11px}
|
||||||
|
.focus-title{font-family:var(--disp);font-size:clamp(26px,2.6vw,40px);line-height:.95;text-transform:uppercase}
|
||||||
|
.focus-desc{font-size:14px;line-height:1.55;font-weight:500}
|
||||||
|
.focus-status{align-self:flex-start;font-family:var(--mono);font-size:10px;letter-spacing:.14em;background:#000;color:var(--yellow);padding:6px 10px;transform:rotate(-1.5deg);margin-top:auto}
|
||||||
|
.now-backburner{margin-top:28px;display:flex;justify-content:space-between;gap:14px;flex-wrap:wrap;border-top:2px solid #000;padding-top:16px;
|
||||||
|
font-family:var(--mono);font-size:11px;letter-spacing:.1em;text-transform:uppercase}
|
||||||
|
|
||||||
|
.repeat-rows{border-top:1px solid var(--line)}
|
||||||
|
.rep{display:grid;grid-template-columns:56px minmax(0,1.1fr) minmax(0,1fr) auto;gap:20px;align-items:baseline;padding:20px 8px;border-bottom:1px solid var(--line);transition:padding-left .35s var(--ease)}
|
||||||
|
.rep:hover{padding-left:22px}
|
||||||
|
.rep-no{font-family:var(--mono);font-size:11px;color:var(--mut)}
|
||||||
|
.rep-main{min-width:0}
|
||||||
|
.rep-artist{display:block;font-weight:700;font-size:clamp(17px,2vw,24px);letter-spacing:-.01em}
|
||||||
|
.rep-work{display:block;font-family:var(--serif);font-style:italic;color:var(--mut);font-size:15px;margin-top:2px}
|
||||||
|
.rep-track{font-family:var(--mono);font-size:11px;color:var(--mut)}
|
||||||
|
.rep-count{font-family:var(--mono);font-size:11px;letter-spacing:.12em;white-space:nowrap}
|
||||||
|
.repeat-foot{margin-top:22px;font-family:var(--mono);font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:var(--mut)}
|
||||||
|
.repeat-foot a{border-bottom:1px solid var(--pink);transition:color .25s}
|
||||||
|
.repeat-foot a:hover{color:var(--pink)}
|
||||||
|
|
||||||
|
.score{background:#0c0c0b;color:#fff}
|
||||||
|
.score .l-no{background:var(--yellow);color:#000}
|
||||||
|
.score .l-tag{background:var(--yellow);color:#000}
|
||||||
|
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));border:2px solid #fff}
|
||||||
|
.stat{padding:clamp(26px,3.4vw,46px) clamp(20px,2.6vw,36px);border-right:2px solid #fff;position:relative;overflow:hidden;isolation:isolate}
|
||||||
|
.stat:last-child{border-right:none}
|
||||||
|
.stat::before{content:'';position:absolute;inset:0;background:var(--sc,var(--yellow));transform:translateY(101%);transition:transform .45s var(--ease);z-index:-1}
|
||||||
|
.stat:hover::before{transform:translateY(0)}
|
||||||
|
.stat-n{display:block;font-family:var(--disp);font-size:clamp(52px,7.5vw,110px);line-height:.95;color:var(--sc,var(--yellow));transition:color .3s}
|
||||||
|
.stat:hover .stat-n{color:#000}
|
||||||
|
.stat-l{display:block;margin-top:12px;font-family:var(--mono);font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:rgba(255,255,255,.55);transition:color .3s}
|
||||||
|
.stat:hover .stat-l{color:#000}
|
||||||
|
|
||||||
|
.devlog .entries{border-top:1px solid var(--line)}
|
||||||
|
.entry{display:grid;grid-template-columns:90px minmax(0,1fr) auto;gap:22px;align-items:baseline;padding:24px 8px;border-bottom:1px solid var(--line);position:relative;transition:padding-left .35s var(--ease)}
|
||||||
|
.entry:hover{padding-left:26px}
|
||||||
|
.entry::before{content:'✳';position:absolute;left:2px;color:var(--pink);opacity:0;transition:opacity .3s;font-size:14px}
|
||||||
|
.entry:hover::before{opacity:1}
|
||||||
|
.entry-year{font-family:var(--mono);font-size:11px;color:var(--mut)}
|
||||||
|
.entry-title{font-weight:700;font-size:clamp(18px,2.4vw,30px);letter-spacing:-.015em}
|
||||||
|
.entry-tags{font-family:var(--mono);font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--mut);white-space:nowrap}
|
||||||
|
|
||||||
|
.contact{background:var(--cyan)}
|
||||||
|
.contact .l-title{font-size:clamp(76px,19vw,270px);text-shadow:7px 7px 0 #000}
|
||||||
|
.contact .sayhi .ch{display:inline-block;animation:floaty 2.6s ease-in-out infinite;animation-delay:calc(var(--i)*.09s)}
|
||||||
|
@keyframes floaty{0%,100%{transform:translateY(0)}50%{transform:translateY(-.07em)}}
|
||||||
|
.contact-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:clamp(36px,5vw,80px);align-items:start;margin-top:clamp(36px,5vw,60px)}
|
||||||
|
.contact-copy p{max-width:44ch;font-weight:500;font-size:clamp(16px,1.8vw,21px);line-height:1.5}
|
||||||
|
.contact-copy p em{font-family:var(--serif);font-style:italic;font-size:1.1em}
|
||||||
|
.magnet{display:inline-flex;align-items:center;gap:14px;margin-top:34px;background:#000;color:var(--cyan);
|
||||||
|
font-family:var(--mono);font-weight:700;font-size:clamp(14px,1.7vw,19px);letter-spacing:.03em;
|
||||||
|
padding:20px 30px;border:2px solid #000;box-shadow:6px 6px 0 #fff;will-change:transform}
|
||||||
|
.magnet:hover{box-shadow:10px 10px 0 #fff}
|
||||||
|
.socials{border-top:2px solid #000}
|
||||||
|
.soc{display:flex;justify-content:space-between;align-items:center;gap:14px;padding:17px 4px;border-bottom:2px solid #000;
|
||||||
|
font-family:var(--mono);font-weight:700;font-size:clamp(13px,1.5vw,17px);text-transform:uppercase;letter-spacing:.05em;transition:padding-left .3s var(--ease),background .3s}
|
||||||
|
.soc:hover{padding-left:18px;background:rgba(0,0,0,.08)}
|
||||||
|
.soc span:last-child{font-weight:400;font-size:12px}
|
||||||
|
|
||||||
|
.foot{display:flex;justify-content:space-between;gap:14px;flex-wrap:wrap;
|
||||||
|
padding:26px 5vw;border-top:1px solid var(--line);
|
||||||
|
font-family:var(--mono);font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--mut)}
|
||||||
|
.foot b{color:var(--pink)}
|
||||||
|
.foot-links a{transition:color .25s}
|
||||||
|
.foot-links a:hover{color:var(--pink)}
|
||||||
|
|
||||||
|
html.js .rv{opacity:0;transform:translateY(28px);transition:opacity .9s var(--ease) var(--d,0s),transform .9s var(--ease) var(--d,0s)}
|
||||||
|
html.js .rv.is-in{opacity:1;transform:none}
|
||||||
|
html.js .chip{opacity:0}
|
||||||
|
html.nf .chip,html.rm .chip{opacity:1}
|
||||||
|
.bt .bw{display:inline-block;opacity:0;filter:blur(10px);transform:translateY(.35em);
|
||||||
|
transition:opacity .7s var(--ease) calc(var(--i)*22ms),filter .7s var(--ease) calc(var(--i)*22ms),transform .7s var(--ease) calc(var(--i)*22ms)}
|
||||||
|
.bt.is-in .bw{opacity:1;filter:blur(0);transform:none}
|
||||||
|
.shine{background:linear-gradient(110deg,currentColor 42%,var(--pink) 50%,currentColor 58%);background-size:240% 100%;
|
||||||
|
-webkit-background-clip:text;background-clip:text;color:transparent;animation:shine 3.4s linear infinite}
|
||||||
|
@keyframes shine{to{background-position:-240% 0}}
|
||||||
|
|
||||||
|
html.nf .rv,html.rm .rv{opacity:1;transform:none}
|
||||||
|
html.nf .bt .bw,html.rm .bt .bw{opacity:1;filter:none;transform:none}
|
||||||
|
|
||||||
|
body.loud-mode{--paper:#101010;--ink:#F4F1EA;--mut:#8f8c83;--line:rgba(244,241,234,.17)}
|
||||||
|
body.loud-mode .q-title,body.loud-mode .about-state{color:var(--paper)}
|
||||||
|
body.loud-mode .q-title{text-shadow:4px 4px 0 var(--acc,var(--pink))}
|
||||||
|
body.loud-mode .row::before{background:var(--pink)}
|
||||||
|
body.loud-mode .row:hover .row-title{color:#000}
|
||||||
|
body.loud-mode .row:hover .row-desc,body.loud-mode .row:hover .row-meta,body.loud-mode .row:hover .row-no{color:rgba(0,0,0,.65)}
|
||||||
|
body.loud-mode .pill:hover{background:var(--pink);border-color:var(--pink);color:#000}
|
||||||
|
body.loud-mode .loader{background:#101010;color:#F4F1EA}
|
||||||
|
|
||||||
|
@media(max-width:960px){
|
||||||
|
.vu{display:none}
|
||||||
|
.about-grid,.contact-grid,.hero-low{grid-template-columns:1fr}
|
||||||
|
.focus-grid{grid-template-columns:1fr}
|
||||||
|
}
|
||||||
|
@media(max-width:760px){
|
||||||
|
.nav-links{display:none}
|
||||||
|
.row{grid-template-columns:40px minmax(0,1fr) 34px}
|
||||||
|
.row-meta{display:none}
|
||||||
|
.entry{grid-template-columns:60px minmax(0,1fr)}
|
||||||
|
.entry-tags{display:none}
|
||||||
|
.q-tag{display:none}
|
||||||
|
.rep{grid-template-columns:40px minmax(0,1fr) auto}
|
||||||
|
.rep-track{display:none}
|
||||||
|
.stat{border-right:none;border-bottom:2px solid #fff}
|
||||||
|
.stat:last-child{border-bottom:none}
|
||||||
|
}
|
||||||
|
@media(prefers-reduced-motion:reduce){
|
||||||
|
*,*::before,*::after{animation-duration:.01ms !important;animation-iteration-count:1 !important;transition-duration:.01ms !important}
|
||||||
|
html{scroll-behavior:auto}
|
||||||
|
html.js .rv{opacity:1;transform:none}
|
||||||
|
.bt .bw{opacity:1;filter:none;transform:none}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<noscript><style>
|
||||||
|
.loader{display:none}
|
||||||
|
.rv,.chip{opacity:1 !important;transform:none !important}
|
||||||
|
.bt .bw{opacity:1;filter:none;transform:none}
|
||||||
|
</style></noscript>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="loader" id="loader" aria-hidden="true">
|
||||||
|
<div class="loader-top"><span>PORTFOLIO — VOL.09</span><span>SOFT ⇄ LOUD</span></div>
|
||||||
|
<div class="loader-mid" id="loaderWord">SOFT</div>
|
||||||
|
<div class="loader-bot">
|
||||||
|
<div class="loader-count"><span id="loaderNum">000</span><small>%</small></div>
|
||||||
|
<div class="loader-bar"><i id="loaderBar"></i></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<header class="nav" id="nav">
|
||||||
|
<a class="brand" href="#top">M—WA <span>©26</span></a>
|
||||||
|
<nav class="nav-links" aria-label="Sections">
|
||||||
|
<a href="#about" data-scramble>Identity</a>
|
||||||
|
<a href="#loadout" data-scramble>Loadout</a>
|
||||||
|
<a href="#builds" data-scramble>Builds</a>
|
||||||
|
<a href="#now" data-scramble>Now</a>
|
||||||
|
<a href="#devlog" data-scramble>Devlog</a>
|
||||||
|
<a href="#contact" data-scramble>Contact</a>
|
||||||
|
</nav>
|
||||||
|
<label class="switch">
|
||||||
|
<input type="checkbox" id="loudToggle" aria-label="Force loud mode">
|
||||||
|
<span class="sw-track"><i class="sw-thumb"></i></span>
|
||||||
|
<span class="sw-txt" id="swTxt">AUTO</span>
|
||||||
|
</label>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<aside class="vu" id="vu" aria-hidden="true">
|
||||||
|
<span class="vu-cap">S</span>
|
||||||
|
<div class="vu-track"><div class="vu-fill" id="vuFill"></div><div class="vu-needle" id="vuNeedle"></div></div>
|
||||||
|
<span class="vu-cap">L</span>
|
||||||
|
<div class="vu-read" id="vuRead">SOFT</div>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<main id="top">
|
||||||
|
|
||||||
|
<section class="q hero" data-vibe="0.18">
|
||||||
|
<div class="hero-eyebrow" id="heroEyebrow">
|
||||||
|
<span>Portfolio — Vol.09</span>
|
||||||
|
<span class="shine">Modern Simplistic Maximalism</span>
|
||||||
|
<span>milesallen.site</span>
|
||||||
|
</div>
|
||||||
|
<h1 class="hero-name" id="heroName">
|
||||||
|
<span class="ln" data-split>MILES</span>
|
||||||
|
<span class="ln outline" data-split>WOLF ALLEN</span>
|
||||||
|
<span class="hero-star" aria-hidden="true">*</span>
|
||||||
|
</h1>
|
||||||
|
<div class="hero-low">
|
||||||
|
<p class="hero-state" id="heroState">I make games, tools & websites that feel <em>good</em> to use.</p>
|
||||||
|
<div class="hero-side" id="heroSide">
|
||||||
|
<span class="side-status"><i class="dot"></i>Usually online</span>
|
||||||
|
<div class="side-row"><span>Direction</span><b>scroll ↓</b></div>
|
||||||
|
<div class="side-row"><span>Public repos</span><b>160+</b></div>
|
||||||
|
<div class="side-row"><span>Loud mode</span><b>press L</b></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="band" aria-hidden="true">
|
||||||
|
<div class="marquee">
|
||||||
|
<div class="mq-set">
|
||||||
|
<span>Building emulators</span><span class="ast">*</span>
|
||||||
|
<span class="ghost">Send help</span><span class="ast">*</span>
|
||||||
|
<span>160 repos and counting</span><span class="ast">*</span>
|
||||||
|
<span class="ghost">Still debugging</span><span class="ast">*</span>
|
||||||
|
<span>Geometry Dash addict</span><span class="ast">*</span>
|
||||||
|
<span class="ghost">Usually online</span><span class="ast">*</span>
|
||||||
|
</div>
|
||||||
|
<div class="mq-set">
|
||||||
|
<span>Building emulators</span><span class="ast">*</span>
|
||||||
|
<span class="ghost">Send help</span><span class="ast">*</span>
|
||||||
|
<span>160 repos and counting</span><span class="ast">*</span>
|
||||||
|
<span class="ghost">Still debugging</span><span class="ast">*</span>
|
||||||
|
<span>Geometry Dash addict</span><span class="ast">*</span>
|
||||||
|
<span class="ghost">Usually online</span><span class="ast">*</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section class="q about" id="about" data-vibe="0.08" style="--acc:var(--pink)">
|
||||||
|
<header class="q-head rv">
|
||||||
|
<span class="q-no">01</span>
|
||||||
|
<h2 class="q-title">Who's Miles</h2>
|
||||||
|
<span class="q-tag">Identity — Soft</span>
|
||||||
|
</header>
|
||||||
|
<div class="about-grid">
|
||||||
|
<div class="about-copy">
|
||||||
|
<p class="about-state bt">Software should feel <em>good</em> to use — so I build emulators, games & tools until it does.</p>
|
||||||
|
<p class="rv">I build things on the internet. Some of them are games, some are tools, and some exist purely because I thought it would be fun. <b>160 repos and 91 stars later</b>, I'm still figuring it out as I go.</p>
|
||||||
|
<p class="rv" style="--d:.08s">Right now I'm co-founder of <b>Diskette Labs</b>, building <b>Gelectron</b> (a full Electron replacement), and running <b>FEDL</b> — the Geometry Dash demon list. Still in school, somehow.</p>
|
||||||
|
<div class="about-links rv" style="--d:.14s">
|
||||||
|
<a class="pill" href="https://milesallen.site/about-me.html">More about me →</a>
|
||||||
|
<a class="pill" href="https://diskettelabs.com/" target="_blank" rel="noopener">Diskette Labs ↗</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<dl class="facts rv" style="--d:.1s">
|
||||||
|
<div class="fact"><dt>Status</dt><dd><span class="live"></span>usually online</dd></div>
|
||||||
|
<div class="fact"><dt>Crew</dt><dd>Diskette Labs — co-founder</dd></div>
|
||||||
|
<div class="fact"><dt>Badges</dt><dd><span class="badge-chip">Pull Shark ×2</span><span class="badge-chip">YOLO</span><span class="badge-chip">Quickdraw</span></dd></div>
|
||||||
|
<div class="fact"><dt>Believes</dt><dd>pancakes > waffles · cereal is soup</dd></div>
|
||||||
|
<div class="fact"><dt>Stack</dt><dd>JS · TS · Rust · Swift · Python · Blender</dd></div>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="l loadout" id="loadout" data-vibe="1">
|
||||||
|
<header class="l-head">
|
||||||
|
<span class="l-no">02</span>
|
||||||
|
<h2 class="l-title">My<br>Loadout</h2>
|
||||||
|
<span class="l-tag">Skills — Loud</span>
|
||||||
|
</header>
|
||||||
|
<div class="chip-group">Ready to use<i></i>16</div>
|
||||||
|
<div class="chip-wall" id="chipWall" data-io>
|
||||||
|
<span class="chip" data-r="-3">JavaScript</span>
|
||||||
|
<span class="chip" data-r="2">Backend JS</span>
|
||||||
|
<span class="chip" data-r="-1">HTML</span>
|
||||||
|
<span class="chip" data-r="3">CSS</span>
|
||||||
|
<span class="chip" data-r="-2">Python</span>
|
||||||
|
<span class="chip" data-r="1">Git</span>
|
||||||
|
<span class="chip" data-r="-3">Blender</span>
|
||||||
|
<span class="chip" data-r="2">Unity</span>
|
||||||
|
<span class="chip" data-r="-1">Unreal</span>
|
||||||
|
<span class="chip" data-r="3">Cloudflare</span>
|
||||||
|
<span class="chip" data-r="-2">Linux</span>
|
||||||
|
<span class="chip" data-r="1">Netlify</span>
|
||||||
|
<span class="chip" data-r="-3">Bash</span>
|
||||||
|
<span class="chip" data-r="2">GitHub</span>
|
||||||
|
<span class="chip" data-r="-1">Electron</span>
|
||||||
|
<span class="chip" data-r="3">Gelectron</span>
|
||||||
|
</div>
|
||||||
|
<div class="chip-group">Currently unlocking<i></i>4</div>
|
||||||
|
<div class="chip-wall" data-io>
|
||||||
|
<span class="chip locked" data-r="-2">PHP</span>
|
||||||
|
<span class="chip locked" data-r="1">.NET</span>
|
||||||
|
<span class="chip locked" data-r="2">Nix</span>
|
||||||
|
<span class="chip locked" data-r="-1">Yarn</span>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="q builds" id="builds" data-vibe="0.08" style="--acc:var(--cyan)">
|
||||||
|
<header class="q-head rv">
|
||||||
|
<span class="q-no">03</span>
|
||||||
|
<h2 class="q-title">The Builds</h2>
|
||||||
|
<span class="q-tag">Projects — Soft</span>
|
||||||
|
</header>
|
||||||
|
<div class="rows">
|
||||||
|
<a class="row rv" href="https://milesallen.site/projects/omniemu.html"><span class="row-no">01</span><span class="row-main"><span class="row-title">OmniEmu 2.0</span><span class="row-desc">All your emulators in one place. Install 20+, scan ROMs, launch games.</span></span><span class="row-meta">Active · TS · MIT</span><span class="row-arrow">↗</span></a>
|
||||||
|
<a class="row rv" style="--d:.03s" href="https://milesallen.site/projects/gelectron.html"><span class="row-no">02</span><span class="row-main"><span class="row-title">Gelectron</span><span class="row-desc">Drop-in Electron replacement on native web views. No bundled Chromium.</span></span><span class="row-meta">Early · Rust</span><span class="row-arrow">↗</span></a>
|
||||||
|
<a class="row rv" style="--d:.06s" href="https://vanilla.milesallen.site/" target="_blank" rel="noopener"><span class="row-no">03</span><span class="row-main"><span class="row-title">Vanilla</span><span class="row-desc">A clean, minimal web experiment. No frameworks, no cruft.</span></span><span class="row-meta">Unreleased · JS</span><span class="row-arrow">↗</span></a>
|
||||||
|
<a class="row rv" style="--d:.09s" href="https://me.fedl.site/" target="_blank" rel="noopener"><span class="row-no">04</span><span class="row-main"><span class="row-title">FEDL</span><span class="row-desc">The demon list for Geometry Dash. Ranked levels, verified runs.</span></span><span class="row-meta">Live · 3800+ runs</span><span class="row-arrow">↗</span></a>
|
||||||
|
<a class="row rv" style="--d:.12s" href="https://milesallen.site/projects/command-move.html"><span class="row-no">05</span><span class="row-main"><span class="row-title">CommandMove</span><span class="row-desc">True cut-and-paste for Finder. Cmd+X, navigate, Cmd+V.</span></span><span class="row-meta">macOS · Swift</span><span class="row-arrow">↗</span></a>
|
||||||
|
<a class="row rv" style="--d:.15s" href="https://milesallen.site/projects/noto.html"><span class="row-no">06</span><span class="row-main"><span class="row-title">Noto</span><span class="row-desc">Note-taking with AI built in. Powered by Cortex, good at organizing chaos.</span></span><span class="row-meta">Active · Flutter</span><span class="row-arrow">↗</span></a>
|
||||||
|
<a class="row rv" style="--d:.18s" href="https://milesallen.site/projects/nova.html"><span class="row-no">07</span><span class="row-main"><span class="row-title">Nova AI</span><span class="row-desc">Local AI assistant. Desktop GUI, web interface and CLI. No cloud.</span></span><span class="row-meta">Python · MIT</span><span class="row-arrow">↗</span></a>
|
||||||
|
<a class="row rv" style="--d:.21s" href="https://milesallen.site/projects/music-app.html"><span class="row-no">08</span><span class="row-main"><span class="row-title">Music App</span><span class="row-desc">YouTube Music energy without the annoying parts.</span></span><span class="row-meta">JS · Apache-2.0</span><span class="row-arrow">↗</span></a>
|
||||||
|
<a class="row rv" style="--d:.24s" href="https://milesallen.site/projects/rhythm-game.html"><span class="row-no">09</span><span class="row-main"><span class="row-title">Rhythm Game</span><span class="row-desc">Hit keys to the beat. Real-time notes. My fingers hurt.</span></span><span class="row-meta">HTML · MPL-2.0</span><span class="row-arrow">↗</span></a>
|
||||||
|
<a class="row rv" style="--d:.27s" href="https://milesallen.site/projects/geode-mod.html"><span class="row-no">10</span><span class="row-main"><span class="row-title">FEDL Geode Mod</span><span class="row-desc">The demon list, in-game. Roulette mode, banned mod detection.</span></span><span class="row-meta">WIP · Geode</span><span class="row-arrow">↗</span></a>
|
||||||
|
<a class="row rv" style="--d:.30s" href="https://milesallen.site/projects/decompilations.html"><span class="row-no">11</span><span class="row-main"><span class="row-title">Decompilations</span><span class="row-desc">Native PC ports of games that probably shouldn't exist.</span></span><span class="row-meta">HTML · MIT</span><span class="row-arrow">↗</span></a>
|
||||||
|
<a class="row rv" style="--d:.33s" href="https://milesallen.site/projects/issue-tracker.html"><span class="row-no">12</span><span class="row-main"><span class="row-title">Issue Tracker</span><span class="row-desc">Built my own because why not. Node.js, no frameworks, raw determination.</span></span><span class="row-meta">Node.js</span><span class="row-arrow">↗</span></a>
|
||||||
|
<a class="row rv" style="--d:.36s" href="https://milesallen.site/projects/moon-gaming.html"><span class="row-no">13</span><span class="row-main"><span class="row-title">Moon Gaming</span><span class="row-desc">Browser-game hub built at 2am. A growing library of chaos.</span></span><span class="row-meta">1542 commits</span><span class="row-arrow">↗</span></a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="l score" id="score" data-vibe="1">
|
||||||
|
<header class="l-head">
|
||||||
|
<span class="l-no">04</span>
|
||||||
|
<h2 class="l-title">Scoreboard</h2>
|
||||||
|
<span class="l-tag">Numbers — Peak</span>
|
||||||
|
</header>
|
||||||
|
<div class="stats">
|
||||||
|
<div class="stat rv" data-io style="--sc:var(--yellow)"><span class="stat-n" data-count="3800" data-suffix="+">0</span><span class="stat-l">Verified FEDL runs</span></div>
|
||||||
|
<div class="stat rv" data-io style="--sc:var(--cyan);--d:.06s"><span class="stat-n" data-count="20" data-suffix="+">0</span><span class="stat-l">Emulators in OmniEmu</span></div>
|
||||||
|
<div class="stat rv" data-io style="--sc:var(--pink);--d:.12s"><span class="stat-n" data-count="1542">0</span><span class="stat-l">Moon Gaming commits</span></div>
|
||||||
|
<div class="stat rv" data-io style="--sc:var(--green);--d:.18s"><span class="stat-n" data-count="160" data-suffix="+">0</span><span class="stat-l">Public repos</span></div>
|
||||||
|
<div class="stat rv" data-io style="--sc:var(--orange);--d:.24s"><span class="stat-n" data-count="91">0</span><span class="stat-l">Stars earned</span></div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="q devlog" id="devlog" data-vibe="0.08" style="--acc:var(--purple)">
|
||||||
|
<header class="q-head rv">
|
||||||
|
<span class="q-no">05</span>
|
||||||
|
<h2 class="q-title">Devlog</h2>
|
||||||
|
<span class="q-tag">Writing — Soft</span>
|
||||||
|
</header>
|
||||||
|
<div class="entries">
|
||||||
|
<a class="entry rv" href="https://milesallen.site/blog/"><span class="entry-year">2026</span><span class="entry-title">Focusing on Diskette Labs</span><span class="entry-tags">Design · Collective</span></a>
|
||||||
|
<a class="entry rv" style="--d:.05s" href="https://milesallen.site/blog/"><span class="entry-year">2026</span><span class="entry-title">Why I Built Gelectron</span><span class="entry-tags">Rust · Native</span></a>
|
||||||
|
<a class="entry rv" style="--d:.1s" href="https://milesallen.site/blog/"><span class="entry-year">2026</span><span class="entry-title">Why I Built OmniEmu</span><span class="entry-tags">TypeScript · Electron</span></a>
|
||||||
|
<a class="entry rv" style="--d:.15s" href="https://milesallen.site/blog/"><span class="entry-year">2025</span><span class="entry-title">CommandMove: Cut & Paste on Mac</span><span class="entry-tags">Swift · macOS</span></a>
|
||||||
|
<a class="entry rv" style="--d:.2s" href="https://milesallen.site/blog/"><span class="entry-year">2025</span><span class="entry-title">Running the FEDL</span><span class="entry-tags">Node.js · Community</span></a>
|
||||||
|
<a class="entry rv" style="--d:.25s" href="https://milesallen.site/blog/"><span class="entry-year">2025</span><span class="entry-title">Building Nova AI</span><span class="entry-tags">Python · Local AI</span></a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="l now" id="now" data-vibe="1">
|
||||||
|
<header class="l-head">
|
||||||
|
<span class="l-no">06</span>
|
||||||
|
<h2 class="l-title">The Now</h2>
|
||||||
|
<span class="l-tag">Focus — Loud</span>
|
||||||
|
</header>
|
||||||
|
<div class="focus-grid">
|
||||||
|
<a class="focus rv" href="https://vanilla.milesallen.site/" target="_blank" rel="noopener">
|
||||||
|
<span class="focus-no">01</span>
|
||||||
|
<h3 class="focus-title">Building Vanilla</h3>
|
||||||
|
<p class="focus-desc">Pure vanilla JS — no frameworks, no libraries, no cruft. Still unreleased but active, polishing daily. The whole point is proving the raw web can feel good.</p>
|
||||||
|
<span class="focus-status">Active — main project</span>
|
||||||
|
</a>
|
||||||
|
<a class="focus rv" style="--d:.08s" href="https://milesallen.site/projects/gelectron.html">
|
||||||
|
<span class="focus-no">02</span>
|
||||||
|
<h3 class="focus-title">Building Gelectron</h3>
|
||||||
|
<p class="focus-desc">Drop-in Electron replacement on native web views — no 150MB binaries, no 300MB of RAM to show a window. Filling out the API layer: BrowserWindow, IPC, Menu, Tray.</p>
|
||||||
|
<span class="focus-status">Early — side project</span>
|
||||||
|
</a>
|
||||||
|
<a class="focus rv" style="--d:.16s" href="https://me.fedl.site/" target="_blank" rel="noopener">
|
||||||
|
<span class="focus-no">03</span>
|
||||||
|
<h3 class="focus-title">Running the FEDL</h3>
|
||||||
|
<p class="focus-desc">The Geometry Dash demon list is still going strong — nearly 4,000 verified runs and 340+ ranked levels. Building better moderation tools and a cleaner submission pipeline.</p>
|
||||||
|
<span class="focus-status">Ongoing — community</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="now-backburner">
|
||||||
|
<span>Back burner: Noto (it'll come around)</span>
|
||||||
|
<span>This site keeps evolving</span>
|
||||||
|
<span>Updated Aug 1, 2026</span>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="q repeat" id="repeat" data-vibe="0.08" style="--acc:var(--cyan)">
|
||||||
|
<header class="q-head rv">
|
||||||
|
<span class="q-no">07</span>
|
||||||
|
<h2 class="q-title">On Repeat</h2>
|
||||||
|
<span class="q-tag">Listening — Soft</span>
|
||||||
|
</header>
|
||||||
|
<div class="repeat-rows">
|
||||||
|
<div class="rep rv"><span class="rep-no">01</span><span class="rep-main"><span class="rep-artist">Jamie Paige</span><span class="rep-work">Machine Love</span></span><span class="rep-track">Birdbrain (w/ OK Glass)</span><span class="rep-count">12 tracks</span></div>
|
||||||
|
<div class="rep rv" style="--d:.05s"><span class="rep-no">02</span><span class="rep-main"><span class="rep-artist">FLAVOR FOLEY</span><span class="rep-work">Spoken For</span></span><span class="rep-track">BUTCHER VANITY</span><span class="rep-count">7 tracks</span></div>
|
||||||
|
<div class="rep rv" style="--d:.1s"><span class="rep-no">03</span><span class="rep-main"><span class="rep-artist">VocaloKAT</span><span class="rep-work">Singles</span></span><span class="rep-track">Affection Addiction (w/ Aku P)</span><span class="rep-count">4 tracks</span></div>
|
||||||
|
<div class="rep rv" style="--d:.15s"><span class="rep-no">04</span><span class="rep-main"><span class="rep-artist">EROMA</span><span class="rep-work">Singles</span></span><span class="rep-track">POTETO CHIP</span><span class="rep-count">5 tracks</span></div>
|
||||||
|
<div class="rep rv" style="--d:.2s"><span class="rep-no">05</span><span class="rep-main"><span class="rep-artist">AnythingBecomeMoe</span><span class="rep-work">Singles</span></span><span class="rep-track">PingPong TIKITAKA</span><span class="rep-count">3 tracks</span></div>
|
||||||
|
</div>
|
||||||
|
<p class="repeat-foot rv">Vocaloid on loop, Rage Against the Machine on bad days — <a href="https://milesallen.site/music.html">full listening history →</a></p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="l contact" id="contact" data-vibe="1">
|
||||||
|
<header class="l-head">
|
||||||
|
<span class="l-no">08</span>
|
||||||
|
<h2 class="l-title sayhi" id="sayHi">Say Hi</h2>
|
||||||
|
<span class="l-tag">Contact — Loud</span>
|
||||||
|
</header>
|
||||||
|
<div class="contact-grid">
|
||||||
|
<div class="contact-copy">
|
||||||
|
<p class="rv">Unless you're a bot — then please go away. Everyone else: I'm open to collaborations, weird ideas, and <em>very</em> specific bug reports.</p>
|
||||||
|
<a class="magnet" id="mailBtn" href="mailto:[email protected]">[email protected] <span>↗</span></a>
|
||||||
|
</div>
|
||||||
|
<div class="socials rv" style="--d:.1s">
|
||||||
|
<a class="soc" href="https://github.com/mileswolfallen2" target="_blank" rel="noopener"><span data-scramble>GitHub</span><span>mileswolfallen2</span></a>
|
||||||
|
<a class="soc" href="https://replit.com/@MilesAllen1" target="_blank" rel="noopener"><span data-scramble>Replit</span><span>@MilesAllen1</span></a>
|
||||||
|
<a class="soc" href="https://codepen.io/mileswolfallen2-the-typescripter" target="_blank" rel="noopener"><span data-scramble>CodePen</span><span>mileswolfallen2</span></a>
|
||||||
|
<a class="soc" href="https://me.fedl.site" target="_blank" rel="noopener"><span data-scramble>FEDL</span><span>me.fedl.site</span></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer class="foot q" data-vibe="0">
|
||||||
|
<span>© 2026 Miles Wolf Allen</span>
|
||||||
|
<span class="foot-links"><a href="https://milesallen.site/uses.html">Uses</a> · <a href="https://milesallen.site/colophon.html">Colophon</a> · <a href="https://milesallen.site/quotes.html">Quotes</a> · <a href="https://milesallen.site/music.html">Music</a></span>
|
||||||
|
<span>Press <b>L</b> to go loud</span>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/bundles/anime.umd.min.js"></script>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
document.documentElement.classList.add('js');
|
||||||
|
const $=(s,c)=>(c||document).querySelector(s);
|
||||||
|
const $$=(s,c)=>Array.from((c||document).querySelectorAll(s));
|
||||||
|
const RM=matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||||
|
if(RM)document.documentElement.classList.add('rm');
|
||||||
|
const hasAnime=typeof anime!=='undefined';
|
||||||
|
if(!hasAnime||RM)document.documentElement.classList.add('nf');
|
||||||
|
|
||||||
|
function splitChars(el,mask){
|
||||||
|
const text=el.textContent;
|
||||||
|
el.setAttribute('aria-label',text.trim());
|
||||||
|
el.textContent='';
|
||||||
|
const chars=[];
|
||||||
|
text.split('').forEach(ch=>{
|
||||||
|
let c=document.createElement('span');
|
||||||
|
c.className='ch';
|
||||||
|
c.textContent=ch===' '?'\u00A0':ch;
|
||||||
|
c.setAttribute('aria-hidden','true');
|
||||||
|
if(mask){
|
||||||
|
const w=document.createElement('span');
|
||||||
|
w.className='chw';
|
||||||
|
w.appendChild(c);
|
||||||
|
el.appendChild(w);
|
||||||
|
}else{
|
||||||
|
el.appendChild(c);
|
||||||
|
}
|
||||||
|
chars.push(c);
|
||||||
|
});
|
||||||
|
return chars;
|
||||||
|
}
|
||||||
|
|
||||||
|
function splitWords(el){
|
||||||
|
const walk=node=>{
|
||||||
|
Array.from(node.childNodes).forEach(n=>{
|
||||||
|
if(n.nodeType===3){
|
||||||
|
const frag=document.createDocumentFragment();
|
||||||
|
n.textContent.split(/(\s+)/).forEach(part=>{
|
||||||
|
if(!part)return;
|
||||||
|
if(/^\s+$/.test(part)){frag.appendChild(document.createTextNode(' '));return;}
|
||||||
|
const s=document.createElement('span');
|
||||||
|
s.className='bw';
|
||||||
|
s.textContent=part;
|
||||||
|
frag.appendChild(s);
|
||||||
|
});
|
||||||
|
node.replaceChild(frag,n);
|
||||||
|
}else if(n.nodeType===1){walk(n);}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
walk(el);
|
||||||
|
$$('.bw',el).forEach((w,i)=>w.style.setProperty('--i',i));
|
||||||
|
}
|
||||||
|
|
||||||
|
let heroChars1=[],heroChars2=[];
|
||||||
|
if(hasAnime&&!RM){
|
||||||
|
const lns=$$('#heroName [data-split]');
|
||||||
|
heroChars1=splitChars(lns[0],true);
|
||||||
|
heroChars2=splitChars(lns[1],true);
|
||||||
|
}
|
||||||
|
$$('.bt').forEach(splitWords);
|
||||||
|
const sayHi=$('#sayHi');
|
||||||
|
if(sayHi&&!RM){
|
||||||
|
const chars=splitChars(sayHi,false);
|
||||||
|
chars.forEach((c,i)=>c.style.setProperty('--i',i));
|
||||||
|
}
|
||||||
|
|
||||||
|
const loader=$('#loader');
|
||||||
|
function killLoader(){if(loader)loader.remove();}
|
||||||
|
|
||||||
|
function startIntro(){
|
||||||
|
if(!hasAnime||RM)return;
|
||||||
|
const {animate,createTimeline,stagger,spring}=anime;
|
||||||
|
createTimeline({defaults:{ease:'outExpo',duration:900}})
|
||||||
|
.add('#nav',{y:[-26,0],opacity:[0,1]},0)
|
||||||
|
.add('#heroEyebrow',{y:[18,0],opacity:[0,1]},60)
|
||||||
|
.add(heroChars1,{y:['115%','0%'],delay:stagger(36)},120)
|
||||||
|
.add(heroChars2,{y:['115%','0%'],delay:stagger(36)},300)
|
||||||
|
.add('.hero-star',{scale:[0,1],rotate:[-180,0],ease:spring({stiffness:180,damping:13})},650)
|
||||||
|
.add('#heroState',{y:[26,0],opacity:[0,1]},720)
|
||||||
|
.add('.side-status',{scale:[.8,1],opacity:[0,1],ease:spring({stiffness:200,damping:14})},800)
|
||||||
|
.add('.side-row',{y:[14,0],opacity:[0,1],delay:stagger(80)},880)
|
||||||
|
.add('#vu',{opacity:[0,1],x:[24,0]},950);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!hasAnime||RM){
|
||||||
|
killLoader();
|
||||||
|
}else{
|
||||||
|
const {animate}=anime;
|
||||||
|
const num=$('#loaderNum'),bar=$('#loaderBar'),word=$('#loaderWord');
|
||||||
|
const words=['SOFT','LOUD','SOFT','LOUD'];
|
||||||
|
let wi=0;
|
||||||
|
const wordTimer=setInterval(()=>{
|
||||||
|
wi=(wi+1)%words.length;
|
||||||
|
word.textContent=words[wi];
|
||||||
|
word.classList.toggle('is-loud',words[wi]==='LOUD');
|
||||||
|
},300);
|
||||||
|
const state={v:0};
|
||||||
|
animate(state,{
|
||||||
|
v:100,duration:1600,ease:'inOutQuad',
|
||||||
|
onUpdate:()=>{
|
||||||
|
num.textContent=String(Math.round(state.v)).padStart(3,'0');
|
||||||
|
bar.style.transform='scaleX('+(state.v/100)+')';
|
||||||
|
},
|
||||||
|
onComplete:()=>{
|
||||||
|
clearInterval(wordTimer);
|
||||||
|
animate(loader,{
|
||||||
|
y:['0%','-102%'],duration:850,ease:'inOutQuint',delay:120,
|
||||||
|
onComplete:()=>{killLoader();startIntro();}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setTimeout(()=>{if(document.body.contains(loader))killLoader();},4000);
|
||||||
|
}
|
||||||
|
|
||||||
|
const io=new IntersectionObserver(entries=>{
|
||||||
|
entries.forEach(en=>{
|
||||||
|
if(!en.isIntersecting)return;
|
||||||
|
const el=en.target;
|
||||||
|
el.classList.add('is-in');
|
||||||
|
const counter=$('[data-count]',el);
|
||||||
|
if(counter)runCount(counter);
|
||||||
|
if(el.classList.contains('chip-wall'))burstChips(el);
|
||||||
|
io.unobserve(el);
|
||||||
|
});
|
||||||
|
},{threshold:.18,rootMargin:'0px 0px -6% 0px'});
|
||||||
|
$$('.rv,.bt,[data-io]').forEach(el=>io.observe(el));
|
||||||
|
|
||||||
|
function runCount(el){
|
||||||
|
const target=parseFloat(el.dataset.count)||0;
|
||||||
|
const suffix=el.dataset.suffix||'';
|
||||||
|
if(!hasAnime||RM){el.textContent=target.toLocaleString()+suffix;return;}
|
||||||
|
const {animate}=anime;
|
||||||
|
const o={n:0};
|
||||||
|
animate(o,{
|
||||||
|
n:target,duration:1700,ease:'outExpo',
|
||||||
|
onUpdate:()=>{el.textContent=Math.round(o.n).toLocaleString()+suffix;}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function burstChips(wall){
|
||||||
|
const chips=$$('.chip',wall);
|
||||||
|
if(!hasAnime||RM){
|
||||||
|
chips.forEach(c=>c.style.rotate=c.dataset.r+'deg');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const {animate,stagger,spring}=anime;
|
||||||
|
animate(chips,{
|
||||||
|
scale:[0,1],y:[44,0],
|
||||||
|
rotate:(el,i)=>[parseFloat(el.dataset.r)-14,parseFloat(el.dataset.r)],
|
||||||
|
delay:stagger(34),
|
||||||
|
ease:spring({stiffness:210,damping:14})
|
||||||
|
});
|
||||||
|
animate(chips,{opacity:[0,1],duration:420,delay:stagger(34),ease:'outQuad'});
|
||||||
|
}
|
||||||
|
|
||||||
|
const SCRAMBLE='ABCDEFGHIJKLMNOPQRSTUVWXYZ#*+/<>_';
|
||||||
|
$$('[data-scramble]').forEach(a=>{
|
||||||
|
if(RM)return;
|
||||||
|
const orig=a.textContent;
|
||||||
|
let raf=null,it=0,total=Math.max(12,orig.length*2);
|
||||||
|
a.addEventListener('mouseenter',()=>{
|
||||||
|
if(raf)cancelAnimationFrame(raf);
|
||||||
|
it=0;
|
||||||
|
const tick=()=>{
|
||||||
|
it++;
|
||||||
|
const reveal=Math.floor((it/total)*orig.length);
|
||||||
|
a.textContent=orig.split('').map((c,i)=>{
|
||||||
|
if(c===' ')return ' ';
|
||||||
|
return i<reveal?c:SCRAMBLE[Math.random()*SCRAMBLE.length|0];
|
||||||
|
}).join('');
|
||||||
|
if(it<total){raf=requestAnimationFrame(tick);}
|
||||||
|
else{a.textContent=orig;raf=null;}
|
||||||
|
};
|
||||||
|
tick();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$$('.magnet').forEach(btn=>{
|
||||||
|
if(RM||!hasAnime)return;
|
||||||
|
const {animate,spring}=anime;
|
||||||
|
let raf=null;
|
||||||
|
btn.addEventListener('mousemove',e=>{
|
||||||
|
const r=btn.getBoundingClientRect();
|
||||||
|
const dx=e.clientX-(r.left+r.width/2);
|
||||||
|
const dy=e.clientY-(r.top+r.height/2);
|
||||||
|
if(raf)cancelAnimationFrame(raf);
|
||||||
|
raf=requestAnimationFrame(()=>{
|
||||||
|
animate(btn,{x:dx*.3,y:dy*.3,duration:320,ease:'out'});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
btn.addEventListener('mouseleave',()=>{
|
||||||
|
if(raf)cancelAnimationFrame(raf);
|
||||||
|
animate(btn,{x:0,y:0,duration:750,ease:spring({stiffness:170,damping:12})});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if(!RM){
|
||||||
|
const sparkColors=['var(--pink)','var(--yellow)','var(--cyan)','var(--green)'];
|
||||||
|
document.addEventListener('pointerdown',e=>{
|
||||||
|
if(e.button!==0||!hasAnime)return;
|
||||||
|
const {animate}=anime;
|
||||||
|
for(let i=0;i<8;i++){
|
||||||
|
const p=document.createElement('i');
|
||||||
|
p.className='spark';
|
||||||
|
p.style.background=sparkColors[i%sparkColors.length];
|
||||||
|
p.style.left=e.clientX+'px';
|
||||||
|
p.style.top=e.clientY+'px';
|
||||||
|
document.body.appendChild(p);
|
||||||
|
const ang=(i/8)*Math.PI*2;
|
||||||
|
animate(p,{
|
||||||
|
x:[0,Math.cos(ang)*52],
|
||||||
|
y:[0,Math.sin(ang)*52],
|
||||||
|
opacity:[1,0],scale:[1,.15],
|
||||||
|
duration:560,ease:'outCubic',
|
||||||
|
onComplete:()=>p.remove()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const body=document.body;
|
||||||
|
const toggle=$('#loudToggle');
|
||||||
|
const swTxt=$('#swTxt');
|
||||||
|
function setLoud(on){
|
||||||
|
body.classList.toggle('loud-mode',on);
|
||||||
|
toggle.checked=on;
|
||||||
|
swTxt.textContent=on?'LOUD':'AUTO';
|
||||||
|
swTxt.classList.toggle('on',on);
|
||||||
|
vuTarget=on?1:currentVibe();
|
||||||
|
}
|
||||||
|
toggle.addEventListener('change',()=>setLoud(toggle.checked));
|
||||||
|
addEventListener('keydown',e=>{
|
||||||
|
if(e.metaKey||e.ctrlKey||e.altKey)return;
|
||||||
|
const tag=(document.activeElement&&document.activeElement.tagName)||'';
|
||||||
|
if(/INPUT|TEXTAREA|SELECT/.test(tag))return;
|
||||||
|
if(e.key.toLowerCase()==='l'){setLoud(!body.classList.contains('loud-mode'));}
|
||||||
|
});
|
||||||
|
|
||||||
|
const vu=$('#vu'),vuFill=$('#vuFill'),vuNeedle=$('#vuNeedle'),vuRead=$('#vuRead');
|
||||||
|
let vuTarget=0,vuCur=0;
|
||||||
|
function currentVibe(){
|
||||||
|
const mid=innerHeight*.5;
|
||||||
|
let val=null;
|
||||||
|
$$('[data-vibe]').forEach(s=>{
|
||||||
|
const r=s.getBoundingClientRect();
|
||||||
|
if(r.top<=mid&&r.bottom>=mid)val=parseFloat(s.dataset.vibe);
|
||||||
|
});
|
||||||
|
return val===null?vuTarget:val;
|
||||||
|
}
|
||||||
|
vuTarget=currentVibe();
|
||||||
|
addEventListener('scroll',()=>{
|
||||||
|
if(!body.classList.contains('loud-mode'))vuTarget=currentVibe();
|
||||||
|
},{passive:true});
|
||||||
|
(function vuLoop(){
|
||||||
|
vuCur+=(vuTarget-vuCur)*.07;
|
||||||
|
const pct=(vuCur*100).toFixed(2)+'%';
|
||||||
|
vuFill.style.height=pct;
|
||||||
|
vuNeedle.style.top=pct;
|
||||||
|
vuRead.textContent=vuCur<.33?'SOFT':vuCur<.72?'MID':'LOUD';
|
||||||
|
const c=vuCur<.33?'var(--ink)':vuCur<.72?'var(--orange)':'var(--pink)';
|
||||||
|
vu.style.setProperty('--vu-c',c);
|
||||||
|
requestAnimationFrame(vuLoop);
|
||||||
|
})();
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user