Download index.html from Death242/awsaa: direct link, hf CLI and curl.
- Browser
- Download file 61.2 kB
-
https://huggingface.co/spaces/Death242/awsaa/resolve/main/index.html
- Command line
-
hf download hf://spaces/Death242/awsaa/index.html
-
curl -L -o index.html https://huggingface.co/spaces/Death242/awsaa/resolve/main/index.html
61.2 kB
| <html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"> | |
| <title>Awsaa · Developer</title><meta name="description" content="Awsaa builds real apps, websites and games: Mellow, Noor, VOID, Cramly and more. Made by Legion."> | |
| <meta name="theme-color" content="#ffffff"> | |
| <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=Unbounded:wght@600;800&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet"> | |
| <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 40 40%27%3E%3Crect width=%2740%27 height=%2740%27 rx=%2711%27 fill=%27%23d7192d%27/%3E%3Cpath d=%27M11 30.5 19.2 10.2a1 1 0 0 1 1.6 0L29 30.5%27 fill=%27none%27 stroke=%27white%27 stroke-width=%274.4%27 stroke-linecap=%27round%27 stroke-linejoin=%27round%27/%3E%3Cpath d=%27M15.6 23.6h8.8%27 stroke=%27white%27 stroke-width=%273.8%27 stroke-linecap=%27round%27/%3E%3Ccircle cx=%2731%27 cy=%2710%27 r=%273.1%27 fill=%27white%27/%3E%3C/svg%3E"> | |
| <style> | |
| :root { --bg:#ffffff; --soft:#f7f5f4; --ink:#141416; --mute:#65656e; --line:#e9e5e3; --red:#d7192d; --red-soft:#fdecee; --wide:"Unbounded",Inter,system-ui,sans-serif; --body:Inter,system-ui,sans-serif; } | |
| * { box-sizing:border-box; } html { scroll-behavior:smooth; background:var(--bg); } | |
| body { margin:0; background:var(--bg); color:var(--ink); font:16px/1.6 var(--body); -webkit-font-smoothing:antialiased; overflow-x:hidden; } | |
| a { color:inherit; } button { font:inherit; color:inherit; cursor:pointer; } .wrap { max-width:1120px; margin:0 auto; padding:0 clamp(16px,4vw,32px); } | |
| #bar { position:fixed; top:0; left:0; height:3px; width:0; background:var(--red); z-index:9; } | |
| nav { position:sticky; top:0; z-index:5; background:rgba(255,255,255,.88); backdrop-filter:blur(12px); border-bottom:1px solid var(--line); } | |
| nav .wrap { display:flex; align-items:center; gap:14px; height:62px; position:relative; } | |
| .brand { display:flex; align-items:center; gap:10px; text-decoration:none; } .mark { width:34px; height:34px; transition:transform .3s; } .brand:hover .mark { transform:rotate(-8deg) scale(1.06); } | |
| .mark .dot { transform-origin:31px 10px; animation:pulse 2.4s ease-in-out infinite; } @keyframes pulse { 50% { transform:scale(.72); } } | |
| .word { font:800 19px var(--wide); letter-spacing:-.01em; } .word i { color:var(--red); font-style:normal; } | |
| .made { font-size:12px; color:var(--mute); font-weight:600; border-left:1px solid var(--line); padding-left:12px; } | |
| nav .links { margin-left:auto; display:flex; gap:22px; font-size:14px; font-weight:600; } nav .links a { text-decoration:none; color:var(--mute); } nav .links a:hover, nav .links a.on { color:var(--red); } | |
| .hero { position:relative; padding:clamp(56px,10vw,104px) 0 56px; } | |
| #glow { position:absolute; inset:0; pointer-events:none; background:radial-gradient(420px circle at var(--x,70%) var(--y,30%), rgba(215,25,45,.10), transparent 70%); } | |
| .kicker { display:inline-block; font-size:12px; font-weight:700; letter-spacing:.18em; text-transform:uppercase; color:var(--red); background:var(--red-soft); padding:6px 12px; border-radius:999px; } | |
| h1 { font:800 clamp(40px,7.4vw,92px)/1.02 var(--wide); letter-spacing:-.02em; margin:20px 0 18px; min-height:2.1em; } #type { color:var(--red); } #type::after { content:""; display:inline-block; width:.08em; height:.9em; margin-left:.06em; background:var(--red); vertical-align:-.08em; animation:blink 1s steps(2) infinite; } @keyframes blink { 50% { opacity:0; } } | |
| .lead { font-size:clamp(17px,2vw,21px); color:#3a3a40; max-width:62ch; margin:0; } | |
| .cta { display:flex; gap:12px; flex-wrap:wrap; margin-top:30px; } .btn { text-decoration:none; font-weight:700; font-size:15px; padding:13px 22px; border-radius:999px; border:2px solid var(--red); background:#fff; color:var(--red); transition:transform .15s; } | |
| .btn.solid { background:var(--red); color:#fff; } .btn:hover { transform:translateY(-2px); } | |
| .stats { display:grid; grid-template-columns:repeat(6,minmax(0,1fr)); gap:1px; background:var(--line); border:1px solid var(--line); border-radius:20px; overflow:hidden; margin-top:56px; } | |
| .stats div { background:#fff; padding:22px 18px; } .stats b { display:block; font:800 34px var(--wide); color:var(--red); font-variant-numeric:tabular-nums; } .stats span { font-size:13px; color:var(--mute); } | |
| section { padding:64px 0; } section.alt { background:var(--soft); } h2 { font:800 clamp(28px,4vw,42px)/1.1 var(--wide); margin:0 0 10px; } .sub { color:var(--mute); margin:0 0 28px; max-width:60ch; } | |
| .flag { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:18px; } .grid { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:16px; } | |
| .card { background:#fff; border:1px solid var(--line); border-radius:20px; padding:22px; display:flex; flex-direction:column; gap:12px; cursor:pointer; transition:transform .2s ease, box-shadow .2s, border-color .2s; transform-style:preserve-3d; will-change:transform; } | |
| .card:hover, .card:focus-visible { box-shadow:0 18px 40px rgba(215,25,45,.12); border-color:#f3c3c8; outline:none; } .card.hide { display:none; } | |
| .card .top { display:flex; gap:14px; align-items:flex-start; flex-wrap:wrap; } .card .top .live { flex:none; } .tt { flex:1; min-width:0; } .em { width:46px; height:46px; flex:none; display:grid; place-items:center; font-size:24px; background:var(--red-soft); border-radius:14px; transition:transform .25s; } .card:hover .em { transform:rotate(-8deg) scale(1.08); } | |
| .card h3 { margin:0; font:800 19px var(--wide); } .tag { margin:4px 0 0; color:var(--mute); font-size:14px; } .about { margin:0; font-size:15px; color:#33333a; } | |
| .how { margin:0; font-size:13.5px; color:#3a3a40; border-left:3px solid var(--red); padding-left:12px; } | |
| .stack { display:flex; flex-wrap:wrap; gap:6px; margin-top:auto; } .stack span { font-size:12px; font-weight:600; padding:4px 10px; border-radius:999px; background:var(--soft); border:1px solid var(--line); color:#45454d; } | |
| .more { font-weight:700; font-size:14px; color:var(--red); } | |
| .live { display:flex; align-items:center; gap:6px; font-size:11px; font-weight:700; color:var(--mute); white-space:nowrap; } .live i { width:8px; height:8px; border-radius:50%; background:#c9c5c3; } | |
| .live.up i { background:#18a957; box-shadow:0 0 0 4px rgba(24,169,87,.15); } .live.up b { color:#128a46; } | |
| .tools { display:flex; gap:10px; flex-wrap:wrap; align-items:center; margin-bottom:14px; } .searchrow { display:flex; gap:12px; align-items:center; margin-bottom:22px; } | |
| .chip { border:1px solid var(--line); background:#fff; border-radius:999px; padding:8px 16px; font-size:14px; font-weight:600; transition:all .15s; } .chip.on { background:var(--red); border-color:var(--red); color:#fff; } .chip:not(.on):hover { border-color:var(--red); color:var(--red); } | |
| #q { flex:1; max-width:460px; border:1px solid var(--line); border-radius:999px; padding:11px 18px; font:inherit; font-size:15px; outline:none; -webkit-appearance:none; } #q:focus { border-color:var(--red); box-shadow:0 0 0 4px var(--red-soft); } | |
| #count { font-size:13px; color:var(--mute); } | |
| .tl { position:relative; background:#fff; border:1px solid var(--line); border-radius:20px; padding:18px 18px 10px; } | |
| .tl .row { display:grid; grid-template-columns:130px 1fr; align-items:center; gap:12px; height:30px; cursor:pointer; } .tl .row:hover .nm { color:var(--red); } | |
| .tl .nm { font-size:13px; font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; } .tl .track { position:relative; height:12px; } | |
| .tl .seg { position:absolute; top:0; height:12px; border-radius:6px; background:var(--red); opacity:.75; transform-origin:left; transform:scaleX(0); transition:transform .8s cubic-bezier(.2,.8,.2,1), opacity .2s; } .tl.in .seg { transform:scaleX(1); } .tl .row:hover .seg { opacity:1; } | |
| .tl .ticks { display:grid; grid-template-columns:130px 1fr; gap:12px; margin-top:6px; } .tl .ticks div { display:flex; justify-content:space-between; font-size:11px; color:var(--mute); } | |
| #tip { position:fixed; pointer-events:none; background:var(--ink); color:#fff; font-size:12px; padding:6px 10px; border-radius:8px; opacity:0; transition:opacity .12s; z-index:20; } | |
| .how-it { display:grid; grid-template-columns:1.1fr 1fr; gap:28px; align-items:start; } | |
| .flow { display:grid; grid-template-columns:1fr auto 1fr auto 1fr; gap:10px; align-items:center; } .flow .box { background:#fff; border:1px solid var(--line); border-radius:16px; padding:16px; transition:all .2s; cursor:default; } | |
| .flow .box:hover, .flow .box.hl { border:2px solid var(--red); background:var(--red-soft); transform:translateY(-3px); } .flow h4 { margin:0 0 6px; font:800 14px var(--wide); } .flow p { margin:0; font-size:13px; color:var(--mute); } .flow .arrow { color:var(--red); font-weight:800; } | |
| table { width:100%; border-collapse:collapse; background:#fff; border:1px solid var(--line); border-radius:16px; overflow:hidden; } th, td { text-align:left; padding:12px 16px; border-bottom:1px solid var(--line); font-size:14px; vertical-align:top; transition:background .2s; } th { width:34%; color:var(--red); } | |
| tr.hl th, tr.hl td { background:var(--red-soft); } | |
| .skills { display:grid; grid-template-columns:repeat(5,minmax(0,1fr)); gap:16px; } .skills div { border-top:3px solid var(--red); padding-top:14px; } .skills h4 { margin:0 0 6px; font:800 15px var(--wide); } .skills p { margin:0; font-size:14px; color:#3a3a40; } | |
| .lesson { margin-top:28px; background:#fff; border:1px solid var(--line); border-radius:20px; padding:22px; } .lesson b { color:var(--red); } | |
| dialog { border:0; border-radius:24px; padding:0; width:min(640px,calc(100% - 32px)); max-height:calc(100% - 48px); box-shadow:0 30px 80px rgba(0,0,0,.25); } dialog::backdrop { background:rgba(20,20,22,.45); backdrop-filter:blur(4px); } | |
| dialog[open] { animation:pop .25s cubic-bezier(.2,.8,.2,1); } @keyframes pop { from { opacity:0; transform:translateY(16px) scale(.98); } } | |
| .dlg { padding:26px; } .dlg .top { display:flex; gap:14px; align-items:center; margin-bottom:14px; } .dlg h3 { margin:0; font:800 24px var(--wide); } .dlg .x { margin-left:auto; border:0; background:var(--soft); width:36px; height:36px; border-radius:50%; font-size:18px; } | |
| .dlg h4 { font:800 13px var(--wide); letter-spacing:.06em; text-transform:uppercase; color:var(--red); margin:20px 0 8px; } .dlg ol { margin:0; padding-left:20px; color:#33333a; font-size:14.5px; } .dlg li { margin-bottom:6px; } | |
| .dlg .acts { display:flex; gap:10px; flex-wrap:wrap; margin-top:22px; } | |
| footer { border-top:1px solid var(--line); padding:28px 0 40px; color:var(--mute); font-size:14px; } footer .wrap { display:flex; align-items:center; gap:14px; flex-wrap:wrap; } footer b { color:var(--ink); } | |
| .rv { opacity:0; transform:translateY(18px); transition:opacity .6s ease, transform .6s ease; } .rv.in { opacity:1; transform:none; } | |
| .hire { background:linear-gradient(180deg,#fff, var(--red-soft)); } .svcs { display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:16px; } | |
| .svc { background:#fff; border:1px solid var(--line); border-radius:20px; padding:22px; display:flex; flex-direction:column; gap:10px; } .svc h3 { margin:0; font:800 17px var(--wide); } .svc p { margin:0; font-size:14px; color:#3a3a40; } | |
| .svc ul { margin:0; padding-left:18px; font-size:13.5px; color:#45454d; } .svc .btn { margin-top:auto; text-align:center; } | |
| .ph { font:800 20px var(--wide); margin:40px 0 14px; } .process { list-style:none; padding:0; margin:0; display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:16px; } | |
| .process li { display:flex; gap:12px; align-items:flex-start; } .process b { flex:none; width:34px; height:34px; border-radius:50%; background:var(--red); color:#fff; display:grid; place-items:center; font:800 15px var(--wide); } | |
| .process h4 { margin:2px 0 4px; font:800 14px var(--wide); } .process p { margin:0; font-size:13.5px; color:#45454d; } | |
| .proof { margin-top:30px; background:#fff; border:1px solid var(--line); border-radius:20px; padding:18px 22px; font-size:15px; } .proof b { color:var(--red); } .proof a { color:var(--red); font-weight:700; } | |
| nav .links a.hl { color:#fff ; background:var(--red); padding:6px 14px; border-radius:999px; } | |
| .dlg label { display:block; font-size:13px; font-weight:700; margin:14px 0 6px; } .dlg input, .dlg textarea { width:100%; border:1px solid var(--line); border-radius:12px; padding:11px 14px; font:inherit; font-size:15px; outline:none; resize:vertical; } | |
| .dlg input:focus, .dlg textarea:focus { border-color:var(--red); box-shadow:0 0 0 4px var(--red-soft); } .needs { display:flex; flex-wrap:wrap; gap:8px; } | |
| @media (max-width:1024px) { .svcs, .process { grid-template-columns:repeat(2,minmax(0,1fr)); } } | |
| @media (max-width:560px) { .svcs, .process { grid-template-columns:minmax(0,1fr); } nav .links a.hl { margin:10px clamp(16px,4vw,32px); text-align:center; } } | |
| .menu { display:none; margin-left:auto; width:42px; height:42px; border:1px solid var(--line); border-radius:12px; background:#fff; position:relative; } | |
| .menu span { position:absolute; left:11px; right:11px; height:2px; background:var(--ink); border-radius:2px; transition:transform .2s; } .menu span:first-child { top:15px; } .menu span:last-child { top:24px; } | |
| .menu[aria-expanded="true"] span:first-child { transform:translateY(4.5px) rotate(45deg); } .menu[aria-expanded="true"] span:last-child { transform:translateY(-4.5px) rotate(-45deg); } | |
| .share { display:grid; grid-template-columns:1fr auto; gap:32px; align-items:center; } .qr { background:#fff; border:1px solid var(--line); border-radius:20px; padding:14px; line-height:0; } .qr svg { width:200px; height:200px; } | |
| .small { font-size:13px; color:var(--mute); margin-top:14px; } | |
| #up { position:fixed; right:18px; bottom:calc(18px + env(safe-area-inset-bottom)); width:46px; height:46px; border-radius:50%; border:0; background:var(--red); color:#fff; font-size:20px; font-weight:800; box-shadow:0 10px 26px rgba(215,25,45,.3); opacity:0; pointer-events:none; transform:translateY(10px); transition:all .2s; z-index:6; } | |
| #up.on { opacity:1; pointer-events:auto; transform:none; } | |
| #toast { position:fixed; left:50%; bottom:calc(24px + env(safe-area-inset-bottom)); transform:translate(-50%,20px); background:var(--ink); color:#fff; padding:10px 18px; border-radius:999px; font-size:14px; font-weight:600; opacity:0; pointer-events:none; transition:all .25s; z-index:30; } #toast.on { opacity:1; transform:translate(-50%,0); } | |
| @media (hover:none) { .card:hover { box-shadow:none; border-color:var(--line); } .card:active { transform:scale(.985); } } | |
| @media (max-width:1024px) and (min-width:901px) { .grid { grid-template-columns:repeat(2,minmax(0,1fr)); } .skills { grid-template-columns:repeat(3,minmax(0,1fr)); } } | |
| @media (max-width:900px) { | |
| .menu { display:block; } nav .links { display:flex ; position:absolute; left:0; right:0; top:62px; flex-direction:column; gap:0; background:#fff; border-bottom:1px solid var(--line); box-shadow:0 18px 30px rgba(0,0,0,.08); max-height:0; overflow:hidden; transition:max-height .3s ease; margin:0; } | |
| nav .links.open { max-height:420px; } nav .links a { padding:15px clamp(16px,4vw,32px); border-top:1px solid var(--line); font-size:16px; color:var(--ink); } | |
| .tools { flex-wrap:nowrap; overflow-x:auto; scrollbar-width:none; -webkit-overflow-scrolling:touch; padding-bottom:4px; } .tools::-webkit-scrollbar { display:none; } .chip { flex:none; } | |
| .searchrow { display:flex; gap:10px; align-items:center; margin:-10px 0 20px; } #q { flex:1; } | |
| .share { grid-template-columns:1fr; } .qr { justify-self:start; } | |
| dialog { width:100%; max-width:100%; margin:auto 0 0; border-radius:24px 24px 0 0; max-height:88vh; } dialog[open] { animation:sheet .3s cubic-bezier(.2,.8,.2,1); } | |
| .dlg { padding:22px 18px calc(22px + env(safe-area-inset-bottom)); } .dlg::before { content:""; display:block; width:44px; height:5px; border-radius:3px; background:var(--line); margin:-8px auto 14px; } | |
| } | |
| @keyframes sheet { from { transform:translateY(100%); } } | |
| @media (max-width:560px) { .cta .btn { flex:1 1 100%; text-align:center; } h1 { min-height:2.2em; } .stats b { font-size:28px; } .tl .ticks div span:nth-child(2), .tl .ticks div span:nth-child(4) { display:none; } .hero { padding-top:40px; } section { padding:48px 0; } } | |
| @media (max-width:900px) { .stats { grid-template-columns:repeat(3,minmax(0,1fr)); } .flag, .how-it { grid-template-columns:minmax(0,1fr); } .grid { grid-template-columns:repeat(2,minmax(0,1fr)); } .skills { grid-template-columns:repeat(2,minmax(0,1fr)); } nav .links { display:none; } #q { margin-left:0; min-width:0; flex:1; } } | |
| @media (max-width:560px) { .stats { grid-template-columns:repeat(2,minmax(0,1fr)); } .grid { grid-template-columns:minmax(0,1fr); } .skills { grid-template-columns:minmax(0,1fr); } .flow { grid-template-columns:1fr; } .flow .arrow { transform:rotate(90deg); justify-self:center; } .made { display:none; } .tl .row, .tl .ticks { grid-template-columns:90px 1fr; } } | |
| @media (prefers-reduced-motion:reduce) { .rv { opacity:1; transform:none; transition:none; } .mark .dot, #type::after { animation:none; } .tl .seg { transform:none; } } | |
| </style></head><body> | |
| <div id="bar"></div><div id="tip"></div> | |
| <nav><div class="wrap"><a class="brand" href="#top" aria-label="Awsaa home"><svg class="mark" viewBox="0 0 40 40" aria-hidden="true"><rect width="40" height="40" rx="11" fill="#d7192d"/><path d="M11 30.5 19.2 10.2a1 1 0 0 1 1.6 0L29 30.5" fill="none" stroke="#fff" stroke-width="4.4" stroke-linecap="round" stroke-linejoin="round"/><path d="M15.6 23.6h8.8" stroke="#fff" stroke-width="3.8" stroke-linecap="round"/><circle class="dot" cx="31" cy="10" r="3.1" fill="#fff"/></svg><span class="word">awsaa<i>.</i></span></a><span class="made">Made by Legion</span> | |
| <div class="links" id="links"><a href="#flagship">Flagships</a><a href="#all">All projects</a><a href="#timeline">Timeline</a><a href="#how">How I build</a><a href="#skills">Skills</a><a href="https://death242-hire-awsaa.static.hf.space" class="hl" target="_blank" rel="noopener">Hire me ↗</a></div> | |
| <button class="menu" id="menu" aria-label="Menu" aria-expanded="false"><span></span><span></span></button></div></nav> | |
| <header class="hero" id="top"><div id="glow"></div><div class="wrap"> | |
| <span class="kicker">Developer · Saudi Arabia</span> | |
| <h1>I build <span id="type">real apps.</span></h1> | |
| <p class="lead">I'm Awsaa. In about six weeks I shipped more than 15 working apps, websites and games: a family safety app used on real phones and PCs, a Quran app, an AI study app, a 3D dream-car game set in real Dammam, and more. I design them, test them and push until they feel right, with Claude Code as my engineering team.</p> | |
| <div class="cta"><a class="btn solid" href="https://death242-hire-awsaa.static.hf.space" target="_blank" rel="noopener">Hire me ↗</a><a class="btn" href="#flagship">See my work</a><a class="btn" href="https://death242-showcase.static.hf.space" target="_blank" rel="noopener">Open the showcase</a><a class="btn" href="https://death242-void.static.hf.space" target="_blank" rel="noopener">Drive my dream car</a></div> | |
| <div class="stats"><div class="rv"><b data-count="17" data-suffix="+">0+</b><span>apps, sites and games</span></div><div class="rv"><b data-count="15" data-suffix="">0</b><span>live on the web</span></div><div class="rv"><b data-count="7" data-suffix="">0</b><span>Android apps</span></div><div class="rv"><b data-count="10" data-suffix="+">0+</b><span>apps using AI</span></div><div class="rv"><b data-count="5" data-suffix="">0</b><span>programming languages</span></div><div class="rv"><b data-count="6" data-suffix="">0</b><span>weeks of building</span></div></div> | |
| </div></header> | |
| <section id="flagship" class="alt"><div class="wrap"><h2>Flagship projects</h2><p class="sub">The four I'm proudest of. Click any card for the full story.</p><div class="flag"><article class="card big rv" data-id="homebase" data-kind="app" data-text="mellow the family app: map, video calls, safety and screen time, honest about every bit. kotlin jetpack compose c# windows tracker webrtc flask postgres" tabindex="0"><div class="top"><div class="em">🏡</div><div class="tt"><h3>Mellow</h3><p class="tag">The family app: map, video calls, safety and screen time, honest about every bit.</p></div><span class="live" data-live><i></i><b></b></span></div><p class="about">Mellow (it started as Homebase) is a family app for Android, Windows and the browser: see where everyone is, message with photos and voice notes, and make video calls that really ring. It has SOS, Safe Walk, safety alerts, meals, plans, pocket money and homework, and it links to Noor so the family reads the Quran together. Parents can see screen time and set limits, but it never feels controlling: everyone sees exactly what is shared, can pause it, and every lock is announced in the family chat.</p><p class="how">Flask + Postgres (Neon), a Kotlin Android app, a web app that works offline and a C# Windows program that updates itself. Calls run on WebRTC.</p><div class="stack"><span>Kotlin</span><span>Jetpack Compose</span><span>C# Windows tracker</span><span>WebRTC</span><span>Flask</span></div><span class="more">Read the story →</span></article><article class="card big rv" data-id="noor" data-kind="app" data-text="noor a complete quran app: read, listen, recite and get checked, even offline. flask javascript pwa web speech service worker android app canvas game" tabindex="0"><div class="top"><div class="em">🌙</div><div class="tt"><h3>Noor</h3><p class="tag">A complete Quran app: read, listen, recite and get checked, even offline.</p></div><span class="live" data-live><i></i><b></b></span></div><p class="about">All 114 surahs with translation in 21 languages, tafsir, tajweed colours and word-by-word meanings, and 20 reciters you can save for offline listening. Recite out loud and Noor marks every mistake, or hide the text for tasmi' and get a score. Sheikh Aws, an AI helper, answers questions about prayer, fasting and duas, and families on Mellow see what everyone read while parents give reading and memorizing tasks. Prayer times, Qibla, Ramadan mode, adhkar and a Quran platform game too.</p><p class="how">A static site for the Quran pages and data, plus a small Flask API for search, Sheikh Aws (OpenAI) and recitation checking with speech-to-text.</p><div class="stack"><span>Flask</span><span>JavaScript PWA</span><span>Web Speech</span><span>Service Worker</span><span>Android app</span></div><span class="more">Read the story →</span></article><article class="card big rv" data-id="void" data-kind="app" data-text="void the hydra lte242, a dream car you can watch, drive and fly through real dammam. three.js web audio openstreetmap javascript static site" tabindex="0"><div class="top"><div class="em">🏎️</div><div class="tt"><h3>VOID</h3><p class="tag">The Hydra LTE242, a dream car you can watch, drive and fly through real Dammam.</p></div><span class="live" data-live><i></i><b></b></span></div><p class="about">A black and red hypercar inspired by the McLaren 765LT, modelled entirely in code. Watch a 44-second film synced to an original phonk track: it takes a 360 loop, launches off a ramp, lifts off on four thrusters and glides into the front seat's dashcam. Then play it: free roam in al-Tubaishi, Dammam, rebuilt from real OpenStreetMap data with traffic, people, a Fly button, missions and a garage.</p><p class="how">Three.js, a car modelled entirely in code, music made live with Web Audio, and al-Tubaishi rebuilt from 3,836 real OpenStreetMap buildings.</p><div class="stack"><span>Three.js</span><span>Web Audio</span><span>OpenStreetMap</span><span>JavaScript</span><span>Static site</span></div><span class="more">Read the story →</span></article><article class="card big rv" data-id="cramly" data-kind="ai" data-text="cramly turn your notes into something you can actually learn. flask postgres openai" tabindex="0"><div class="top"><div class="em">🎓</div><div class="tt"><h3>Cramly</h3><p class="tag">Turn your notes into something you can actually learn.</p></div><span class="live" data-live><i></i><b></b></span></div><p class="about">An AI study app. Drop in notes, a PDF, slides or a photo and get a study plan, flashcards, quizzes and a tutor that knows your material, plus match and swipe games to make it stick.</p><p class="how">Flask + Postgres with OpenAI for plans, tutoring, flashcards and quizzes. Its accounts and owner switches are shared by every other app.</p><div class="stack"><span>Flask</span><span>Postgres</span><span>OpenAI</span></div><span class="more">Read the story →</span></article></div></div></section> | |
| <section id="all"><div class="wrap"><h2>Everything else I built</h2><p class="sub">AI tools, games, a flight simulator, a Minecraft bot and the tools I made to ship faster.</p> | |
| <div class="tools"><button class="chip on" data-kind="all">All</button><button class="chip" data-kind="app">Apps</button><button class="chip" data-kind="ai">AI</button><button class="chip" data-kind="game">Games</button><button class="chip" data-kind="tool">Tools</button></div><div class="searchrow"><input id="q" type="search" placeholder="Search projects or tech, e.g. AI or Three.js (press /)" aria-label="Search projects"><span id="count"></span></div> | |
| <div class="grid" id="grid"><article class="card rv" data-id="scout" data-kind="ai" data-text="scout a map that you can talk to. flask leaflet overture maps openai" tabindex="0"><div class="top"><div class="em">🧭</div><div class="tt"><h3>Scout</h3><p class="tag">A map that you can talk to.</p></div><span class="live" data-live><i></i><b></b></span></div><div class="stack"><span>Flask</span><span>Leaflet</span><span>Overture Maps</span><span>OpenAI</span></div><span class="more">Read the story →</span></article><article class="card rv" data-id="unwritten" data-kind="game" data-text="unwritten a life simulator where the ai types your story live. flask vanilla js openai streaming" tabindex="0"><div class="top"><div class="em">📖</div><div class="tt"><h3>Unwritten</h3><p class="tag">A life simulator where the AI types your story live.</p></div><span class="live" data-live><i></i><b></b></span></div><div class="stack"><span>Flask</span><span>Vanilla JS</span><span>OpenAI streaming</span></div><span class="more">Read the story →</span></article><article class="card rv" data-id="cookify" data-kind="tool" data-text="cookify recipes that already know your allergies. flask vanilla js openai" tabindex="0"><div class="top"><div class="em">🍳</div><div class="tt"><h3>Cookify</h3><p class="tag">Recipes that already know your allergies.</p></div><span class="live" data-live><i></i><b></b></span></div><div class="stack"><span>Flask</span><span>Vanilla JS</span><span>OpenAI</span></div><span class="more">Read the story →</span></article><article class="card rv" data-id="crosswind" data-kind="game" data-text="crosswind a flight simulator that lives in a browser tab. three.js custom shaders flask" tabindex="0"><div class="top"><div class="em">✈️</div><div class="tt"><h3>Crosswind</h3><p class="tag">A flight simulator that lives in a browser tab.</p></div><span class="live" data-live><i></i><b></b></span></div><div class="stack"><span>Three.js</span><span>Custom shaders</span><span>Flask</span></div><span class="more">Read the story →</span></article><article class="card rv" data-id="platter" data-kind="tool" data-text="platter a recipe box with 1,103 recipes. flask vanilla js service worker" tabindex="0"><div class="top"><div class="em">🍽️</div><div class="tt"><h3>Platter</h3><p class="tag">A recipe box with 1,103 recipes.</p></div><span class="live" data-live><i></i><b></b></span></div><div class="stack"><span>Flask</span><span>Vanilla JS</span><span>Service worker</span></div><span class="more">Read the story →</span></article><article class="card rv" data-id="alibi" data-kind="game" data-text="alibi a murder mystery where one of your friends is the killer. flask polling openai" tabindex="0"><div class="top"><div class="em">🕵️</div><div class="tt"><h3>Alibi</h3><p class="tag">A murder mystery where one of your friends is the killer.</p></div><span class="live" data-live><i></i><b></b></span></div><div class="stack"><span>Flask</span><span>Polling</span><span>OpenAI</span></div><span class="more">Read the story →</span></article><article class="card rv" data-id="tripmate" data-kind="ai" data-text="tripmate a travel agent that respects your budget. flask leaflet openai + web search" tabindex="0"><div class="top"><div class="em">🧳</div><div class="tt"><h3>Tripmate</h3><p class="tag">A travel agent that respects your budget.</p></div><span class="live" data-live><i></i><b></b></span></div><div class="stack"><span>Flask</span><span>Leaflet</span><span>OpenAI + web search</span></div><span class="more">Read the story →</span></article><article class="card rv" data-id="womanten" data-kind="app" data-text="womanten a confidence and habits coach with a push-up app lock. kotlin ml kit pose detection flask openai" tabindex="0"><div class="top"><div class="em">🌱</div><div class="tt"><h3>Womanten</h3><p class="tag">A confidence and habits coach with a push-up app lock.</p></div><span class="live" data-live><i></i><b></b></span></div><div class="stack"><span>Kotlin</span><span>ML Kit pose detection</span><span>Flask</span><span>OpenAI</span></div><span class="more">Read the story →</span></article><article class="card rv" data-id="whatif" data-kind="ai" data-text="whatif type a what-if and watch the world change. flask openai streaming" tabindex="0"><div class="top"><div class="em">🌀</div><div class="tt"><h3>Whatif</h3><p class="tag">Type a what-if and watch the world change.</p></div><span class="live" data-live><i></i><b></b></span></div><div class="stack"><span>Flask</span><span>OpenAI</span><span>Streaming</span></div><span class="more">Read the story →</span></article><article class="card rv" data-id="nonsense" data-kind="game" data-text="totally normal website a deliberately terrible website. nothing makes sense. flask vanilla js web audio" tabindex="0"><div class="top"><div class="em">🤪</div><div class="tt"><h3>Totally Normal Website</h3><p class="tag">A deliberately terrible website. Nothing makes sense.</p></div><span class="live" data-live><i></i><b></b></span></div><div class="stack"><span>Flask</span><span>Vanilla JS</span><span>Web Audio</span></div><span class="more">Read the story →</span></article><article class="card rv" data-id="control" data-kind="tool" data-text="cramly control the owner panel for cramly. flask admin api" tabindex="0"><div class="top"><div class="em">🛠️</div><div class="tt"><h3>Cramly Control</h3><p class="tag">The owner panel for Cramly.</p></div><span class="live off"><i></i><b>owner only</b></span></div><div class="stack"><span>Flask</span><span>Admin API</span></div><span class="more">Read the story →</span></article></div></div></section> | |
| <section id="timeline" class="alt"><div class="wrap"><h2>Six weeks, start to now</h2><p class="sub">When I worked on each project. Hover a bar for the dates, click it for the story.</p><div class="tl rv" id="tl"></div></div></section> | |
| <section id="how"><div class="wrap"><h2>How I build</h2><p class="sub">Almost every project follows one pattern. Hover a box to see the tools behind it.</p> | |
| <div class="how-it"><div class="flow"><div class="box" data-layer="device"><h4>Your device</h4><p>Android apps, web pages that work offline, a Windows tracker</p></div><div class="arrow">→</div><div class="box" data-layer="server"><h4>My servers</h4><p>Flask apps, one shared login, the Mellow server</p></div><div class="arrow">→</div><div class="box" data-layer="service"><h4>Services</h4><p>OpenAI, Postgres on Neon, Hugging Face</p></div></div> | |
| <table><tr data-layer="device"><th>Mobile and desktop</th><td>Kotlin Android apps, WebView wrappers (appwrap), a C# Windows tracker</td></tr><tr data-layer="device"><th>Front end</th><td>HTML, CSS, JavaScript, Three.js, Web Audio, service workers</td></tr><tr data-layer="server"><th>Back end</th><td>Python, Flask, gunicorn, SQLite, Postgres (Neon)</td></tr><tr data-layer="server"><th>Real time</th><td>WebRTC calls, long polling for live events</td></tr><tr data-layer="service"><th>AI</th><td>OpenAI for tutors, chat helpers, stories, travel plans and speech-to-text</td></tr><tr data-layer="service"><th>Hosting</th><td>GitHub, Render, Hugging Face static Spaces</td></tr></table></div> | |
| <div class="lesson rv"><b>Lesson learned:</b> on 23 October 2026 every app was suspended when the free hosting plan ran past its 5 GB of bandwidth. I traced it to a database check every 0.6 seconds per device and 55 MB app downloads, then fixed it: downloads moved to Hugging Face, compression on, Mellow resting from midnight to 6 am, and five sites moved to free static hosting.</div> | |
| </div></section> | |
| <section id="skills" class="alt"><div class="wrap"><h2>What I'm good at</h2><p class="sub">Turning an idea into a finished, shipped product, and then making it better again and again.</p><div class="skills"><div class="rv"><h4>Product thinking</h4><p>Knowing what a person needs: a family that wants safety without control, a learner who wants a tutor, a player who wants to drive and fly.</p></div><div class="rv"><h4>Design taste</h4><p>A clear eye for when something is too much, clunky or not realistic, and a consistent style across every project.</p></div><div class="rv"><h4>Directing AI</h4><p>Working with Claude Code like a lead with an engineering team: clear asks, fast feedback and high standards.</p></div><div class="rv"><h4>Shipping</h4><p>Real addresses, real Android apps, real updates and real users, not mock-ups.</p></div><div class="rv"><h4>Range</h4><p>Family safety, religion and learning, AI tools, games, 3D, music, maps and Windows software.</p></div></div></div></section> | |
| <section id="share"><div class="wrap share"> | |
| <div><h2>Share my profile</h2><p class="sub">Scan it, send it, or copy the link.</p> | |
| <div class="cta"><button class="btn solid" id="shareBtn">Share</button><button class="btn" id="copyBtn">Copy link</button></div> | |
| <p class="small">death242-awsaa.static.hf.space</p></div> | |
| <div class="qr rv"><svg xmlns="http://www.w3.org/2000/svg" width="198" height="198" class="segno"><g transform="scale(6)"><path fill="#fff" d="M0 0h33v33h-33z"/><path class="qrline" stroke="#d7192d" d="M2 2.5h7m1 0h1m1 0h1m1 0h7m1 0h1m1 0h7m-29 1h1m5 0h1m3 0h1m1 0h1m1 0h1m1 0h2m1 0h1m2 0h1m5 0h1m-29 1h1m1 0h3m1 0h1m2 0h1m1 0h2m1 0h4m4 0h1m1 0h3m1 0h1m-29 1h1m1 0h3m1 0h1m1 0h3m2 0h4m1 0h1m3 0h1m1 0h3m1 0h1m-29 1h1m1 0h3m1 0h1m1 0h1m3 0h5m5 0h1m1 0h3m1 0h1m-29 1h1m5 0h1m1 0h6m3 0h3m2 0h1m5 0h1m-29 1h7m1 0h1m1 0h1m1 0h1m1 0h1m1 0h1m1 0h1m1 0h1m1 0h7m-21 1h1m1 0h3m1 0h5m-19 1h1m3 0h1m1 0h3m3 0h3m1 0h2m1 0h7m2 0h1m-25 1h1m2 0h3m2 0h5m5 0h7m-29 1h1m1 0h2m1 0h3m1 0h4m2 0h1m1 0h2m1 0h2m1 0h1m4 0h1m-27 1h1m2 0h1m2 0h1m7 0h2m1 0h1m1 0h2m2 0h1m1 0h2m-29 1h2m4 0h1m2 0h3m1 0h2m1 0h2m1 0h1m3 0h1m3 0h1m-28 1h2m2 0h2m8 0h3m1 0h1m2 0h8m-29 1h1m4 0h3m2 0h1m1 0h6m2 0h3m2 0h2m1 0h1m-29 1h1m1 0h1m4 0h1m5 0h1m3 0h5m1 0h1m3 0h2m-26 1h2m1 0h2m1 0h3m1 0h1m1 0h1m1 0h1m1 0h1m7 0h1m-28 1h3m1 0h1m3 0h4m1 0h3m5 0h5m1 0h2m-27 1h2m1 0h3m1 0h1m2 0h1m2 0h4m1 0h1m1 0h1m3 0h1m1 0h1m-27 1h1m1 0h2m1 0h1m3 0h1m2 0h1m2 0h1m3 0h2m4 0h2m-29 1h2m3 0h2m1 0h2m2 0h3m2 0h1m1 0h7m2 0h1m-21 1h2m1 0h1m3 0h6m3 0h1m3 0h1m-29 1h7m1 0h1m3 0h6m1 0h2m1 0h1m1 0h3m1 0h1m-29 1h1m5 0h1m2 0h1m2 0h2m1 0h6m3 0h1m2 0h1m-28 1h1m1 0h3m1 0h1m1 0h1m4 0h1m1 0h1m1 0h1m1 0h7m1 0h1m-28 1h1m1 0h3m1 0h1m2 0h1m1 0h1m1 0h4m1 0h1m1 0h1m7 0h1m-29 1h1m1 0h3m1 0h1m2 0h1m3 0h1m1 0h2m2 0h2m4 0h4m-29 1h1m5 0h1m4 0h2m4 0h2m1 0h4m1 0h1m1 0h2m-29 1h7m1 0h1m1 0h2m2 0h2m1 0h4m1 0h4m1 0h1"/></g></svg> | |
| </div> | |
| </div></section> | |
| <button id="up" aria-label="Back to top">↑</button><div id="toast" role="status"></div> | |
| <footer><div class="wrap"><svg class="mark" viewBox="0 0 40 40" aria-hidden="true"><rect width="40" height="40" rx="11" fill="#d7192d"/><path d="M11 30.5 19.2 10.2a1 1 0 0 1 1.6 0L29 30.5" fill="none" stroke="#fff" stroke-width="4.4" stroke-linecap="round" stroke-linejoin="round"/><path d="M15.6 23.6h8.8" stroke="#fff" stroke-width="3.8" stroke-linecap="round"/><circle class="dot" cx="31" cy="10" r="3.1" fill="#fff"/></svg><span><b>Made by Legion.</b> Built with Claude Code. VOID's map data © OpenStreetMap contributors, ODbL.</span></div></footer> | |
| <dialog id="dlg"><div class="dlg" id="dlgBody"></div></dialog> | |
| <script> | |
| const P = {"homebase": {"name": "Mellow", "emoji": "🏡", "tag": "The family app: map, video calls, safety and screen time, honest about every bit.", "about": "Mellow (it started as Homebase) is a family app for Android, Windows and the browser: see where everyone is, message with photos and voice notes, and make video calls that really ring. It has SOS, Safe Walk, safety alerts, meals, plans, pocket money and homework, and it links to Noor so the family reads the Quran together. Parents can see screen time and set limits, but it never feels controlling: everyone sees exactly what is shared, can pause it, and every lock is announced in the family chat.", "stack": ["Kotlin", "Jetpack Compose", "C# Windows tracker", "WebRTC", "Flask", "Postgres"], "url": "https://homebase-family.onrender.com", "apk": "https://homebase-family.onrender.com/download", "steps": ["Open it in the browser, or install the Android app", "Create a family, or join with a code", "Share the map, call, and see screen time"], "made": ["I wanted a family app that never feels controlling, then renamed it Mellow with a calmer look", "I set the rules: consent first, honest screen time, nothing secret. Claude helped write the Android app, the Windows tracker and the server", "We tested on my real laptop and tablet, fixed what broke (grounding, ringing, two tabs), and I shipped it"], "ai": "", "kind": "app", "tl": {"start": "2026-10-10", "end": "2026-10-18", "commits": 52}}, "noor": {"name": "Noor", "emoji": "🌙", "tag": "A complete Quran app: read, listen, recite and get checked, even offline.", "about": "All 114 surahs with translation in 21 languages, tafsir, tajweed colours and word-by-word meanings, and 20 reciters you can save for offline listening. Recite out loud and Noor marks every mistake, or hide the text for tasmi' and get a score. Sheikh Aws, an AI helper, answers questions about prayer, fasting and duas, and families on Mellow see what everyone read while parents give reading and memorizing tasks. Prayer times, Qibla, Ramadan mode, adhkar and a Quran platform game too.", "stack": ["Flask", "JavaScript PWA", "Web Speech", "Service Worker", "Android app", "Canvas game"], "url": "https://death242-noor.static.hf.space", "apk": "https://huggingface.co/datasets/Death242/downloads/resolve/main/noor.apk", "steps": ["Open it on your phone and pick a surah, or tap Listen for Maher Al-Muaiqly", "Tap Recite to read aloud and get checked, or ask Sheikh Aws a question", "Link Mellow so your family sees your streak and can give you tasks"], "made": ["I asked for a Quran reader, then said: make it an app, white and green, that works without internet", "I kept asking for the big features: Maher, recite and get corrected, tasmi' with a score, Ramadan mode, a Mario-style game, Sheikh Aws and family tasks in Mellow. Claude helped build them; the OpenAI API powers Sheikh Aws and the listening in the app", "I tested it, asked for clearer text and a better design, added my key, and put it online"], "ai": "OpenAI API", "kind": "app", "tl": {"start": "2026-10-18", "end": "2026-10-24", "commits": 14}}, "void": {"name": "VOID", "emoji": "🏎️", "tag": "The Hydra LTE242, a dream car you can watch, drive and fly through real Dammam.", "about": "A black and red hypercar inspired by the McLaren 765LT, modelled entirely in code. Watch a 44-second film synced to an original phonk track: it takes a 360 loop, launches off a ramp, lifts off on four thrusters and glides into the front seat's dashcam. Then play it: free roam in al-Tubaishi, Dammam, rebuilt from real OpenStreetMap data with traffic, people, a Fly button, missions and a garage.", "stack": ["Three.js", "Web Audio", "OpenStreetMap", "JavaScript", "Static site"], "url": "https://death242-void.static.hf.space", "apk": null, "steps": ["Press Watch for the film, with sound on", "Press Play, drive out of Tubaishi park, then press F to fly", "Press Build to paint your own Hydra LTE242"], "made": ["I showed Claude a short video of a car concept website and said: make one like it, but original and cooler", "I picked the look (all black and red, like a McLaren 765LT), named it the Hydra LTE242, and asked for a loop, a ramp, thrusters, wings, a front seat and a dashcam. Claude helped build it in Three.js, with phonk-style music made in code", "I asked for a game too: free roam in al-Tubaishi, Dammam, built from real map data, with traffic, people and a Fly button. Then I tested it and pushed until it felt right"], "ai": "", "kind": "app", "tl": {"start": "2026-10-23", "end": "2026-10-24", "commits": 5}}, "scout": {"name": "Scout", "emoji": "🧭", "tag": "A map that you can talk to.", "about": "A local-search map with an AI concierge. Ask for what you want in plain words and it finds real places near you, using a local index of 103,000 Saudi places so answers come back in about a second.", "stack": ["Flask", "Leaflet", "Overture Maps", "OpenAI"], "url": "https://scout-15t6.onrender.com", "apk": "https://scout-15t6.onrender.com/static/scout.apk", "steps": ["Type what you want, like a quiet cafe with wifi", "The concierge searches real places near you", "Tap a pin for hours and directions"], "made": ["I wanted a map I could talk to", "I chose the data and the look. Claude helped build the place index and concierge", "I tuned it until it was fast, then put it online"], "ai": "OpenAI API", "kind": "ai", "tl": {"start": "2026-09-14", "end": "2026-10-16", "commits": 25}}, "unwritten": {"name": "Unwritten", "emoji": "📖", "tag": "A life simulator where the AI types your story live.", "about": "A BitLife-style game. Every year the AI writes an event for your life, word by word as you watch, and the choices you make change your money, health, relationships and ending.", "stack": ["Flask", "Vanilla JS", "OpenAI streaming"], "url": "https://unwritten-dulm.onrender.com", "apk": null, "steps": ["Be born and pick a name", "Watch each year's event type itself out", "Choose, age up, and see how it ends"], "made": ["I asked for BitLife, but written by AI", "I designed the game. Claude helped with the rules and live typing; the OpenAI API writes each event", "I played it, gave feedback, and we balanced it"], "ai": "OpenAI API", "kind": "game", "tl": {"start": "2026-09-19", "end": "2026-10-10", "commits": 12}}, "cookify": {"name": "Cookify", "emoji": "🍳", "tag": "Recipes that already know your allergies.", "about": "A recipe site with an allergy and diet engine, ingredient swaps, and a step-by-step cook mode, so what you see is only what you can safely eat.", "stack": ["Flask", "Vanilla JS", "OpenAI"], "url": "https://cookify-6auu.onrender.com", "apk": null, "steps": ["Tell it your allergies and diet", "Search, or let the AI find recipes", "Cook along in cook mode"], "made": ["I wanted recipes that respect allergies", "I planned the features. Claude helped build the allergy engine; the OpenAI API powers AI search", "I tested it and put it online"], "ai": "OpenAI API", "kind": "tool", "tl": {"start": "2026-09-24", "end": "2026-10-10", "commits": 10}}, "crosswind": {"name": "Crosswind", "emoji": "✈️", "tag": "A flight simulator that lives in a browser tab.", "about": "A flight sim with a real flight model: lift, stall, drag and thrust in SI units on a 16 km generated island. Four aircraft, hand-written sky, sea and clouds, no installs.", "stack": ["Three.js", "Custom shaders", "Flask"], "url": "https://death242-crosswind.static.hf.space", "apk": null, "steps": ["Pick one of the four aircraft", "Full throttle, then ease back at about 60 knots", "Fly the island and land on the runway"], "made": ["I said: make me a plane sim like MSFS", "I picked the aircraft and the feel. Claude helped with the flight physics and the sky", "I flew it, said what felt wrong, and we fixed it"], "ai": "", "kind": "game", "tl": {"start": "2026-09-22", "end": "2026-10-10", "commits": 12}}, "platter": {"name": "Platter", "emoji": "🍽️", "tag": "A recipe box with 1,103 recipes.", "about": "A three-pane recipe box in the style of Plate It. Diet filters, an AI helper, price estimates, a weekly planner, pantry, and it works offline.", "stack": ["Flask", "Vanilla JS", "Service worker"], "url": "https://platter-87b0.onrender.com", "apk": "https://platter-87b0.onrender.com/static/platter.apk", "steps": ["Browse more than a thousand recipes", "Filter by diet, or ask the AI helper", "Plan the week and get a shopping list"], "made": ["I found an app I loved and wanted my own", "I sent the screenshot. Claude helped rebuild it and add 1,103 recipes", "I chose the extras and put it online"], "ai": "OpenAI API", "kind": "tool", "tl": {"start": "2026-09-25", "end": "2026-10-16", "commits": 29}}, "alibi": {"name": "Alibi", "emoji": "🕵️", "tag": "A murder mystery where one of your friends is the killer.", "about": "A multiplayer party game. The AI writes a fresh case, one player is secretly the killer, and everyone plays on their own phone: question suspects, search rooms, and solve it before time runs out.", "stack": ["Flask", "Polling", "OpenAI"], "url": "https://alibi-d9tn.onrender.com", "apk": "https://alibi-d9tn.onrender.com/static/alibi.apk", "steps": ["Make a room and share the code", "Roles are handed out in secret", "Question, search and name the killer"], "made": ["I wanted a murder mystery where a friend is the killer", "I came up with the rules. Claude helped build the multiplayer server; the OpenAI API writes each case", "I played it with bots and friends, then shipped it"], "ai": "OpenAI API", "kind": "game", "tl": {"start": "2026-09-26", "end": "2026-10-16", "commits": 32}}, "tripmate": {"name": "Tripmate", "emoji": "🧳", "tag": "A travel agent that respects your budget.", "about": "An AI trip planner. Say where you are going and what you can spend and it researches flights and stays, checks the total against your budget, and shows the route on a map.", "stack": ["Flask", "Leaflet", "OpenAI + web search"], "url": "https://tripmate-yecg.onrender.com", "apk": null, "steps": ["Say where, when and your budget", "The agent researches flights and stays", "Compare, save, and export to your calendar"], "made": ["I started in ChatGPT Agent Builder and built the agent myself", "Claude taught me step by step, then helped move my agent into code with the OpenAI API", "I added the extras I wanted and put it online"], "ai": "ChatGPT Agent Builder + OpenAI API", "kind": "ai", "tl": {"start": "2026-09-27", "end": "2026-10-10", "commits": 20}}, "cramly": {"name": "Cramly", "emoji": "🎓", "tag": "Turn your notes into something you can actually learn.", "about": "An AI study app. Drop in notes, a PDF, slides or a photo and get a study plan, flashcards, quizzes and a tutor that knows your material, plus match and swipe games to make it stick.", "stack": ["Flask", "Postgres", "OpenAI"], "url": "https://cramly-twz5.onrender.com", "apk": null, "steps": ["Upload notes, a PDF or a photo", "Get a plan, flashcards and quizzes", "Ask the tutor and play the games"], "made": ["I wanted a study app that was actually good", "I designed the study flow. Claude helped build it on the OpenAI API", "I asked for more features each round until it felt right"], "ai": "OpenAI API", "kind": "ai", "tl": {"start": "2026-10-09", "end": "2026-10-15", "commits": 20}}, "womanten": {"name": "Womanten", "emoji": "🌱", "tag": "A confidence and habits coach with a push-up app lock.", "about": "An app for building confidence and healthy habits: an AI coach, a weekly plan, AI-made workout and food plans, habits, a journal, a cycle tracker and challenges with friends. On Android, TikTok, Instagram and YouTube stay locked until you do a push-up on camera, and each push-up earns 10 scrolls. It never rates looks and never counts calories.", "stack": ["Kotlin", "ML Kit pose detection", "Flask", "OpenAI"], "url": "https://womanten.onrender.com", "apk": null, "steps": ["Open the site, or install the Android app", "Pick what you want to work on and build your week", "Lock your scrolling apps and earn scrolls with push-ups"], "made": ["I wanted an app like Critique AI, mixing the confidence coach and the habit tracker, and I named it Womanten", "I decided what it must never do (rate looks, count calories) and what it must do: lock the scrolling apps until you do a push-up. Claude helped build it; the OpenAI API writes the plans and coaches", "I asked for it to be more useful, tested it, fixed what felt wrong, and put it online"], "ai": "OpenAI API", "kind": "app", "tl": {"start": "2026-10-13", "end": "2026-10-13", "commits": 8}}, "whatif": {"name": "Whatif", "emoji": "🌀", "tag": "Type a what-if and watch the world change.", "about": "An alternate-history storyteller. Ask any what-if and the AI writes a timeline that fills in while you watch, the domino chain of cause and effect, who wins and loses and how believable it is. Change any event to branch a new story, keep them all in your own Multiverse, and share one as a link or a poster.", "stack": ["Flask", "OpenAI", "Streaming"], "url": "https://whatif-kkq1.onrender.com", "apk": null, "steps": ["Type a what-if or pick today's", "Watch the timeline write itself", "Change an event to branch it, then share the link"], "made": ["I asked for website ideas and picked the what-if one", "I decided it should write the story live, let you branch any event and share it as a link. Claude helped build it; the OpenAI API writes the stories", "I tested it on real questions, fixed where the AI slipped, and put it online"], "ai": "OpenAI API", "kind": "ai", "tl": {"start": "2026-10-15", "end": "2026-10-15", "commits": 1}}, "nonsense": {"name": "Totally Normal Website", "emoji": "🤪", "tag": "A deliberately terrible website. Nothing makes sense.", "about": "A joke site with a bad UI on purpose: a runaway button, a potato to chat with, a calculator where 2+2 is fish, a download bar for more RAM and a dozen more toys. It opens with a photosensitivity warning and has a Calm mode with no flashing or movement.", "stack": ["Flask", "Vanilla JS", "Web Audio"], "url": "https://death242-totally-normal-website.static.hf.space", "apk": null, "steps": ["Read the warning and pick Calm mode or the chaos", "Press anything. Most things do something", "Try to catch the button. It gives up eventually"], "made": ["I wanted the worst website I could think of, on purpose", "I chose every bad idea: the colours, the runaway button, the potato, the nonsense answers", "I asked for a seizure warning and a Calm mode, tested the buttons until they worked, and put it online"], "ai": "", "kind": "game", "tl": {"start": "2026-10-13", "end": "2026-10-13", "commits": 2}}, "control": {"name": "Cramly Control", "emoji": "🛠️", "tag": "The owner panel for Cramly.", "about": "A separate private panel for running Cramly: see accounts, send a personal message with a unique link, ban or time out an account, and flip a kill switch. Owner only.", "stack": ["Flask", "Admin API"], "url": null, "apk": null, "steps": ["Sign in with the owner password", "See accounts and message people", "Ban, time out, or use the kill switch"], "made": ["I wanted a private owner panel for Cramly", "I decided what it could do. Claude helped build it", "I set the password and put it online"], "ai": "", "kind": "tool", "tl": {"start": "2026-10-09", "end": "2026-10-15", "commits": 16}}}, WORDS = ["real apps.", "games.", "AI tools.", "3D worlds.", "family apps.", "a Quran app."], REDUCED = matchMedia("(prefers-reduced-motion: reduce)").matches; | |
| const $ = s => document.querySelector(s), $$ = s => [...document.querySelectorAll(s)], esc = s => String(s ?? "").replace(/[&<>"]/g, c => ({"&": "&", "<": "<", ">": ">", '"': """})[c]); | |
| // the headline types what I build | |
| (() => { const el = $("#type"); if (REDUCED) return; let w = 0, i = WORDS[0].length, del = true; | |
| const tick = () => { const word = WORDS[w]; if (del) { i--; if (i <= 0) { del = false; w = (w + 1) % WORDS.length; } } else { i++; if (i >= WORDS[w].length) { del = true; el.textContent = WORDS[w]; return setTimeout(tick, 1800); } } | |
| el.textContent = WORDS[w].slice(0, Math.max(0, i)); setTimeout(tick, del ? 45 : 80); }; | |
| setTimeout(tick, 2200); })(); | |
| // a red glow follows the pointer in the hero; a bar shows how far down the page you are | |
| $(".hero").addEventListener("pointermove", ev => { const r = ev.currentTarget.getBoundingClientRect(); $("#glow").style.setProperty("--x", (ev.clientX - r.left) + "px"); $("#glow").style.setProperty("--y", (ev.clientY - r.top) + "px"); }); | |
| addEventListener("scroll", () => { const h = document.documentElement; $("#bar").style.width = (h.scrollTop / Math.max(1, h.scrollHeight - h.clientHeight) * 100) + "%"; | |
| const ids = ["flagship", "all", "timeline", "how", "skills", "share"]; let on = ""; for (const id of ids) if (document.getElementById(id).getBoundingClientRect().top < 140) on = id; | |
| $$("nav .links a").forEach(a => a.classList.toggle("on", a.getAttribute("href") === "#" + on)); }, { passive: true }); | |
| // reveal on scroll, count the stats up, grow the timeline | |
| const io = new IntersectionObserver(es => es.forEach(en => { if (!en.isIntersecting) return; const t = en.target; t.classList.add("in"); io.unobserve(t); | |
| const b = t.querySelector("[data-count]"); if (b) { const n = +b.dataset.count, s = b.dataset.suffix, t0 = performance.now(); const step = now => { const k = Math.min(1, (now - t0) / 1100); b.textContent = Math.round(n * (1 - Math.pow(1 - k, 3))) + s; if (k < 1) requestAnimationFrame(step); }; REDUCED ? b.textContent = n + s : requestAnimationFrame(step); } }), { threshold: .15 }); | |
| $$(".rv").forEach(el => io.observe(el)); | |
| // menu on phones and tablets | |
| const menu = $("#menu"), links = $("#links"); | |
| menu.onclick = () => { const open = !links.classList.contains("open"); links.classList.toggle("open", open); menu.setAttribute("aria-expanded", open); }; | |
| links.querySelectorAll("a").forEach(a => a.addEventListener("click", () => { links.classList.remove("open"); menu.setAttribute("aria-expanded", "false"); })); | |
| // share, copy, back to top, "/" focuses search | |
| const toast = t => { const el = $("#toast"); el.textContent = t; el.classList.add("on"); clearTimeout(toast.t); toast.t = setTimeout(() => el.classList.remove("on"), 2000); }; | |
| const URL_ = "https://death242-awsaa.static.hf.space"; | |
| $("#shareBtn").onclick = async () => { if (navigator.share) { try { await navigator.share({ title: "Awsaa · Developer", text: "Apps, games and AI tools made by Legion", url: URL_ }); } catch { } } else { try { await navigator.clipboard.writeText(URL_); toast("Link copied"); } catch { prompt("Copy the link", URL_); } } }; | |
| $("#copyBtn").onclick = async () => { try { await navigator.clipboard.writeText(URL_); toast("Link copied"); } catch { prompt("Copy the link", URL_); } }; | |
| $("#up").onclick = () => scrollTo({ top: 0, behavior: REDUCED ? "auto" : "smooth" }); | |
| addEventListener("scroll", () => $("#up").classList.toggle("on", scrollY > 900), { passive: true }); | |
| addEventListener("keydown", ev => { if (ev.key === "/" && document.activeElement !== $("#q")) { ev.preventDefault(); $("#q").focus(); } if (ev.key === "Escape" && links.classList.contains("open")) menu.click(); }); | |
| $$(".flow .box").forEach(b => b.addEventListener("click", () => { const on = !b.classList.contains("hl"); $$(".flow .box").forEach(x => x.classList.remove("hl")); $$("tr[data-layer]").forEach(r => r.classList.remove("hl")); if (on) { b.classList.add("hl"); $$(`tr[data-layer="${b.dataset.layer}"]`).forEach(r => r.classList.add("hl")); } })); | |
| // cards tilt toward the pointer (mouse only: on touch it fights the scroll) | |
| const FINE = matchMedia("(hover: hover) and (pointer: fine)").matches; | |
| $$(".card").forEach(c => { if (REDUCED || !FINE) return; c.addEventListener("pointermove", ev => { const r = c.getBoundingClientRect(), x = (ev.clientX - r.left) / r.width - .5, y = (ev.clientY - r.top) / r.height - .5; c.style.transform = `perspective(900px) rotateY(${x * 6}deg) rotateX(${-y * 6}deg) translateY(-3px)`; }); | |
| c.addEventListener("pointerleave", () => { c.style.transform = ""; }); }); | |
| // filter by kind, search by name or tech | |
| let kind = "all"; | |
| const filter = () => { const q = $("#q").value.trim().toLowerCase(); let n = 0; $$("#grid .card").forEach(c => { const ok = (kind === "all" || c.dataset.kind === kind) && (!q || c.dataset.text.includes(q)); c.classList.toggle("hide", !ok); n += ok; }); $("#count").textContent = n + (n === 1 ? " project" : " projects"); }; | |
| $$(".chip").forEach(b => b.onclick = () => { kind = b.dataset.kind; $$(".chip").forEach(x => x.classList.toggle("on", x === b)); filter(); }); | |
| $("#q").addEventListener("input", filter); filter(); | |
| // the story of a project | |
| function openProject(id) { const p = P[id]; if (!p) return; const steps = (p.made && p.made.length ? p.made : []).map(s => `<li>${esc(s)}</li>`).join(""), use = (p.steps || []).map(s => `<li>${esc(s)}</li>`).join(""); | |
| $("#dlgBody").innerHTML = `<div class="top"><div class="em">${esc(p.emoji)}</div><div><h3>${esc(p.name)}</h3><div class="tag">${esc(p.tag)}</div></div><button class="x" aria-label="Close" onclick="dlg.close()">×</button></div> | |
| <p class="about">${esc(p.about)}</p>${steps ? `<h4>How I made it</h4><ol>${steps}</ol>` : ""}${use ? `<h4>Try it</h4><ol>${use}</ol>` : ""} | |
| <div class="stack" style="margin-top:18px">${(p.stack || []).map(s => `<span>${esc(s)}</span>`).join("")}${p.ai ? `<span>AI: ${esc(p.ai)}</span>` : ""}</div> | |
| <div class="acts">${p.url ? `<a class="btn solid" href="${esc(p.url)}" target="_blank" rel="noopener">Open ${esc(p.name)} ↗</a>` : ""}${p.apk ? `<a class="btn" href="${esc(p.apk)}">Android app</a>` : ""}</div>`; | |
| dlg.showModal(); } | |
| const dlg = $("#dlg"); dlg.addEventListener("click", ev => { if (ev.target === dlg) dlg.close(); }); | |
| $$(".card").forEach(c => { c.onclick = () => openProject(c.dataset.id); c.onkeydown = ev => { if (ev.key === "Enter") openProject(c.dataset.id); }; }); | |
| // where each site lives: the free static ones are always on; a browser cannot see another site's status, so Render ones just say so | |
| $$(".card").forEach(c => { const p = P[c.dataset.id], dot = c.querySelector("[data-live]"); if (!p || !p.url || !dot) return; | |
| if (p.url.includes(".static.hf.space")) { dot.classList.add("up"); dot.querySelector("b").textContent = "always on"; } else dot.querySelector("b").textContent = "Render: may be asleep"; }); | |
| // the timeline, drawn from the project dates | |
| (() => { const rows = Object.entries(P).filter(([, p]) => p.tl).sort((a, b) => a[1].tl.start.localeCompare(b[1].tl.start)), t = d => Date.parse(d) / 864e5; | |
| const lo = Math.min(...rows.map(([, p]) => t(p.tl.start))), hi = Math.max(...rows.map(([, p]) => t(p.tl.end))) + 1, fmt = d => new Date(d).toLocaleDateString("en-GB", { day: "numeric", month: "short" }); | |
| const el = $("#tl"); el.innerHTML = rows.map(([id, p]) => `<div class="row" data-id="${id}"><span class="nm">${esc(p.emoji)} ${esc(p.name)}</span><div class="track"><span class="seg" style="left:${(t(p.tl.start) - lo) / (hi - lo) * 100}%;width:${Math.max(1.5, (t(p.tl.end) + 1 - t(p.tl.start)) / (hi - lo) * 100)}%"></span></div></div>`).join("") | |
| + `<div class="ticks"><span></span><div>${[0, .25, .5, .75, 1].map(k => `<span>${fmt((lo + k * (hi - lo - 1)) * 864e5)}</span>`).join("")}</div></div>`; | |
| const tip = $("#tip"); | |
| el.querySelectorAll(".row").forEach(r => { const p = P[r.dataset.id]; | |
| r.onmousemove = ev => { tip.textContent = `${p.name} · ${fmt(p.tl.start)} to ${fmt(p.tl.end)} · ${p.tl.commits} saves`; tip.style.left = ev.clientX + 14 + "px"; tip.style.top = ev.clientY + 14 + "px"; tip.style.opacity = 1; }; | |
| r.onmouseleave = () => { tip.style.opacity = 0; }; r.onclick = () => openProject(r.dataset.id); }); })(); | |
| // how I build: a box lights up its tools | |
| $$(".flow .box").forEach(b => { const on = v => $$(`tr[data-layer="${b.dataset.layer}"]`).forEach(r => r.classList.toggle("hl", v)); | |
| b.addEventListener("mouseenter", () => on(true)); b.addEventListener("mouseleave", () => on(false)); }); | |
| </script> | |
| <script src="hub.js" data-app="awsaa" defer></script> | |
| </body></html> |