katwebsite/index.html

2533 lines
90 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>katOS — oiupoyt</title>
<!-- Google AdSense -->
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3876936176422477" crossorigin="anonymous"></script>
<!-- Fonts & Icons: Space Mono & Share Tech Mono nerdy aesthetic -->
<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=Share+Tech+Mono&family=Space+Mono:ital,wght@0,400;0,700;1,400&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" rel="stylesheet">
<style>
/* ── Reset & Brutalist Geometry ─────────────────────────────────────────── */
* {
box-sizing: border-box;
margin: 0;
padding: 0;
border-radius: 0 !important; /* Strict squared-out terminals & windows */
scrollbar-width: none !important;
-ms-overflow-style: none !important;
}
body, button, input, textarea, select, pre, code, p, a, span, div, h1, h2, h3, h4, h5, h6 {
font-family: 'Space Mono', 'Share Tech Mono', monospace;
}
/* Preserve Font Awesome Icons strictly */
.fa, .fas, .far, .fal, .fad, i[class*="fa-"] {
font-family: "Font Awesome 6 Free" !important;
font-weight: 900 !important;
}
.fab, i.fab {
font-family: "Font Awesome 6 Brands" !important;
font-weight: 400 !important;
}
:root {
--bg: #000000;
--panel-bg: rgba(6, 6, 10, 0.22); /* High glass transparency */
--panel-active: rgba(10, 10, 16, 0.35); /* Focused transparency */
--border: rgba(255, 255, 255, 0.12);
--border-hover: rgba(255, 255, 255, 0.35);
--border-active: rgba(255, 255, 255, 0.7);
--text: #f0f0f0;
--text-dim: #666666;
--text-sub: #999999;
--accent: #ffffff;
--glow: 0 0 10px rgba(255, 255, 255, 0.8), 0 0 22px rgba(255, 255, 255, 0.4);
--glow-subtle: 0 0 8px rgba(255, 255, 255, 0.4);
--taskbar-h: 36px;
}
body {
background: var(--bg);
color: var(--text);
font-family: 'Space Mono', 'Share Tech Mono', monospace;
height: 100vh;
width: 100vw;
overflow: hidden;
user-select: none;
-webkit-font-smoothing: antialiased;
}
/* ── Background Engine & Tactical CRT Layer ────────────────────────────── */
.video-bg {
position: fixed;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
z-index: 0;
pointer-events: none;
opacity: 0;
display: none;
transition: opacity 0.5s ease;
filter: contrast(1.15) brightness(0.88);
}
#bgCanvas {
position: fixed;
inset: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 1;
}
.crt-overlay {
position: fixed;
inset: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 2;
background:
radial-gradient(ellipse at center, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0.72) 100%),
repeating-linear-gradient(0deg, rgba(255, 255, 255, 0.012) 0px, rgba(255, 255, 255, 0.012) 1px, transparent 1px, transparent 3px);
}
/* Minimalist On-Screen HUD Notification Toast */
.kat-toast {
position: fixed;
bottom: 48px;
left: 50%;
transform: translateX(-50%) translateY(8px);
background: rgba(10, 10, 14, 0.92);
border: 1px solid rgba(255, 255, 255, 0.35);
box-shadow: 0 0 20px rgba(255, 255, 255, 0.25);
color: #ffffff;
padding: 6px 16px;
font-size: 11px;
letter-spacing: 0.08em;
text-transform: uppercase;
z-index: 99999;
pointer-events: none;
opacity: 0;
transition: all 0.22s ease;
}
.kat-toast.show {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
/* ── Desktop Workspace ───────────────────────────────────────────────────── */
.desktop {
position: relative;
width: 100%;
height: calc(100vh - var(--taskbar-h));
z-index: 2;
overflow: hidden;
}
/* ── Transparent Squared Windows ─────────────────────────────────────────── */
.window {
position: absolute;
background: var(--panel-bg);
border: 1px solid var(--border);
backdrop-filter: blur(14px) saturate(160%);
-webkit-backdrop-filter: blur(14px) saturate(160%);
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.6), 0 0 1px rgba(255, 255, 255, 0.15) inset;
z-index: 10;
display: flex;
flex-direction: column;
transform: translateZ(0);
backface-visibility: hidden;
contain: layout style;
transition: border-color 0.18s ease, box-shadow 0.18s ease, background 0.18s ease;
}
.window.active {
border-color: var(--border-active);
background: var(--panel-active);
box-shadow: 0 14px 50px rgba(0, 0, 0, 0.75), 0 0 14px rgba(255, 255, 255, 0.18), 0 0 1px rgba(255, 255, 255, 0.4) inset;
z-index: 30;
}
.window.minimized {
display: none !important;
}
.window.maximized {
top: 0 !important;
left: 0 !important;
width: 100% !important;
height: 100% !important;
}
/* Titlebar */
.titlebar {
height: 30px;
min-height: 30px;
background: rgba(255, 255, 255, 0.03);
border-bottom: 1px solid var(--border);
padding: 0 10px;
font-size: 11px;
letter-spacing: 0.04em;
cursor: grab;
display: flex;
align-items: center;
justify-content: space-between;
user-select: none;
}
.window.active .titlebar {
background: rgba(255, 255, 255, 0.06);
border-bottom-color: var(--border-active);
}
.titlebar:active {
cursor: grabbing;
}
.title-left {
display: flex;
align-items: center;
gap: 8px;
color: var(--text-sub);
font-weight: 500;
font-size: 11px;
}
.window.active .title-left {
color: #ffffff;
text-shadow: var(--glow-subtle);
}
.title-left i {
font-size: 10px;
opacity: 0.7;
}
/* Remade Minimalist Terminal Navigation Dot */
.window-controls {
display: flex;
align-items: center;
justify-content: center;
}
.win-dot-close {
width: 14px;
height: 14px;
border: 1px solid rgba(255, 255, 255, 0.22);
background: rgba(255, 255, 255, 0.03);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.16s ease;
position: relative;
}
.win-dot-close::before {
content: '';
width: 4px;
height: 4px;
background: rgba(255, 255, 255, 0.55);
box-shadow: 0 0 4px rgba(255, 255, 255, 0.35);
transition: all 0.16s ease;
}
.win-dot-close:hover {
border-color: #ffffff;
background: rgba(255, 255, 255, 0.16);
box-shadow: 0 0 10px rgba(255, 255, 255, 0.45);
}
.win-dot-close:hover::before {
background: #ffffff;
width: 6px;
height: 6px;
box-shadow: 0 0 8px #ffffff;
}
/* Window Body */
.window-body {
flex: 1;
padding: 14px 16px;
font-size: 12px;
line-height: 1.6;
overflow-y: auto;
overflow-x: hidden;
color: #d0d0d0;
background: transparent !important;
}
/* Complete scrollbar removal (clean invisible scrolling) */
* {
scrollbar-width: none !important;
-ms-overflow-style: none !important;
}
*::-webkit-scrollbar,
.window-body::-webkit-scrollbar,
.terminal-body::-webkit-scrollbar,
body::-webkit-scrollbar,
.desktop::-webkit-scrollbar {
display: none !important;
width: 0 !important;
height: 0 !important;
}
/* ── Terminal Styling ────────────────────────────────────────────────────── */
.terminal-body {
font-family: 'Space Mono', 'Share Tech Mono', monospace !important;
font-size: 11px;
line-height: 1.48;
padding: 12px 14px;
color: #e5e5e5;
background: transparent !important;
overflow-y: auto;
overflow-x: hidden;
height: 100%;
user-select: text;
}
.fastfetch-pre {
white-space: pre;
line-height: 1.35;
letter-spacing: 0.2px;
margin-bottom: 12px;
display: block;
}
.term-row {
display: flex;
align-items: center;
gap: 6px;
}
.term-prompt-line {
display: flex;
align-items: center;
gap: 6px;
margin-top: 8px;
}
.term-prompt {
color: #ffffff;
text-shadow: var(--glow-subtle);
font-weight: 600;
flex-shrink: 0;
}
.term-input {
flex: 1;
background: transparent;
border: none;
color: #ffffff;
font-family: inherit;
font-size: 11px;
outline: none;
caret-color: #fff;
}
.term-output {
margin-top: 4px;
margin-bottom: 8px;
color: #aaa;
line-height: 1.5;
white-space: pre-wrap;
}
.term-cmd {
color: #fff;
font-weight: 600;
}
/* Links & Lists in Windows */
.link-item {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 10px;
margin-bottom: 6px;
background: rgba(255, 255, 255, 0.02);
border: 1px solid rgba(255, 255, 255, 0.09);
cursor: pointer;
font-size: 11px;
color: #ddd;
transition: all 0.15s ease;
text-decoration: none;
}
.link-item:last-child {
margin-bottom: 0;
}
.link-item:hover {
background: rgba(255, 255, 255, 0.09);
border-color: #ffffff;
color: #fff;
box-shadow: 0 0 10px rgba(255, 255, 255, 0.15);
transform: translateX(3px);
}
.link-item i {
width: 16px;
text-align: center;
color: #ffffff;
opacity: 0.85;
}
.link-item .badge {
margin-left: auto;
font-size: 10px;
color: var(--text-dim);
border: 1px solid rgba(255, 255, 255, 0.15);
padding: 1px 6px;
background: rgba(0, 0, 0, 0.6);
}
.skill-tag {
display: inline-block;
padding: 3px 8px;
margin: 3px;
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.1);
font-size: 11px;
color: #ccc;
transition: all 0.15s ease;
}
.skill-tag:hover {
border-color: #ffffff;
color: #ffffff;
box-shadow: 0 0 8px rgba(255, 255, 255, 0.3);
}
/* ── Visualizer Container ────────────────────────────────────────────────── */
.viz-container {
width: 100%;
height: 100%;
position: relative;
background: transparent !important;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
}
#vizCanvas {
width: 100%;
height: 100%;
display: block;
background: transparent;
cursor: pointer;
}
.viz-overlay {
position: absolute;
top: 10px;
left: 12px;
display: flex;
align-items: center;
gap: 10px;
pointer-events: none;
font-size: 10px;
letter-spacing: 0.08em;
color: rgba(255, 255, 255, 0.4);
text-transform: uppercase;
}
.viz-mode-badge {
padding: 2px 6px;
border: 1px solid rgba(255, 255, 255, 0.3);
background: rgba(0, 0, 0, 0.6);
color: #ffffff;
text-shadow: var(--glow-subtle);
}
.viz-hint {
position: absolute;
bottom: 10px;
right: 12px;
font-size: 9px;
color: rgba(255, 255, 255, 0.25);
letter-spacing: 0.06em;
text-transform: uppercase;
pointer-events: none;
}
.viz-idle-msg {
position: absolute;
color: rgba(255, 255, 255, 0.25);
font-size: 11px;
letter-spacing: 0.15em;
text-transform: uppercase;
pointer-events: none;
transition: opacity 0.3s ease;
}
/* ── Taskbar ─────────────────────────────────────────────────────────────── */
.taskbar {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
height: var(--taskbar-h);
background: rgba(4, 4, 6, 0.94);
border-top: 1px solid var(--border);
backdrop-filter: blur(20px);
z-index: 100;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 12px;
font-size: 11px;
}
.taskbar-left {
display: flex;
align-items: center;
gap: 10px;
height: 100%;
}
/* Clean KatOS button without icon */
.os-logo-btn {
font-family: 'JetBrains Mono', monospace;
font-weight: 700;
font-size: 12px;
letter-spacing: 0.05em;
color: #ffffff;
text-shadow: var(--glow-subtle);
padding: 4px 10px;
border: 1px solid rgba(255, 255, 255, 0.25);
background: rgba(255, 255, 255, 0.05);
cursor: pointer;
display: flex;
align-items: center;
transition: all 0.15s ease;
}
.os-logo-btn:hover {
background: rgba(255, 255, 255, 0.15);
border-color: #ffffff;
box-shadow: var(--glow-subtle);
}
.taskbar-divider {
width: 1px;
height: 16px;
background: rgba(255, 255, 255, 0.12);
margin: 0 2px;
}
.taskbar-windows {
display: flex;
align-items: center;
gap: 4px;
height: 100%;
}
.taskbar-win-btn {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 10px;
background: rgba(255, 255, 255, 0.02);
border: 1px solid rgba(255, 255, 255, 0.1);
font-size: 11px;
color: var(--text-sub);
cursor: pointer;
max-width: 140px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
transition: all 0.15s ease;
}
.taskbar-win-btn:hover {
background: rgba(255, 255, 255, 0.08);
color: #fff;
border-color: var(--border-hover);
}
.taskbar-win-btn.active {
background: rgba(255, 255, 255, 0.12);
border-color: #ffffff;
color: #fff;
box-shadow: var(--glow-subtle);
}
.taskbar-right {
display: flex;
align-items: center;
gap: 14px;
}
/* Music Player Widget in Taskbar */
.music-bar {
display: flex;
align-items: center;
gap: 8px;
padding: 2px 8px;
border: 1px solid rgba(255, 255, 255, 0.1);
background: rgba(255, 255, 255, 0.02);
}
.music-icon {
font-size: 11px;
color: #ffffff;
animation: spinDisc 3s linear infinite;
}
.music-icon.paused {
animation-play-state: paused;
color: var(--text-dim);
}
@keyframes spinDisc {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.music-track {
max-width: 160px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-size: 10px;
color: var(--text-sub);
}
.player-btn {
background: transparent;
border: 1px solid rgba(255, 255, 255, 0.2);
color: #fff;
width: 20px;
height: 20px;
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-size: 9px;
transition: all 0.15s ease;
}
.player-btn:hover {
background: #fff;
color: #000;
box-shadow: var(--glow-subtle);
}
.volume-slider {
width: 50px;
height: 3px;
-webkit-appearance: none;
appearance: none;
background: rgba(255, 255, 255, 0.25);
outline: none;
}
.volume-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 8px;
height: 12px;
background: #ffffff;
cursor: pointer;
}
/* System Tray Clock */
.tray-time {
color: #ffffff;
font-weight: 600;
letter-spacing: 0.05em;
text-shadow: var(--glow-subtle);
}
/* ── Minimal Boot Screen ─────────────────────────────────────────────────── */
#loadingScreen {
position: fixed;
inset: 0;
background: #000000;
z-index: 10000;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
transition: opacity 0.5s ease;
}
#loadingScreen.hidden {
opacity: 0;
pointer-events: none;
}
.boot-minimal {
text-align: center;
}
.boot-logo {
font-family: 'Space Mono', 'Share Tech Mono', monospace;
font-size: 54px;
font-weight: 700;
letter-spacing: -0.03em;
color: #ffffff;
text-shadow: 0 0 24px rgba(255, 255, 255, 0.85), 0 0 45px rgba(255, 255, 255, 0.35);
margin-bottom: 12px;
}
.boot-sub {
font-family: 'Space Mono', 'Share Tech Mono', monospace;
font-size: 11px;
color: #777777;
letter-spacing: 0.12em;
text-transform: lowercase;
animation: pulseSub 1.8s infinite alternate;
}
@keyframes pulseSub {
from { opacity: 0.4; color: #666; }
to { opacity: 1; color: #ffffff; text-shadow: var(--glow-subtle); }
}
/* ── Desktop Right-Click Context Menu ────────────────────────────────────── */
.context-menu {
position: fixed;
z-index: 9999;
background: rgba(10, 10, 12, 0.96);
border: 1px solid var(--border-active);
box-shadow: 0 10px 30px rgba(0,0,0,0.8);
backdrop-filter: blur(20px);
padding: 4px 0;
min-width: 180px;
display: none;
}
.ctx-item {
padding: 7px 14px;
font-size: 11px;
color: #ddd;
cursor: pointer;
display: flex;
align-items: center;
gap: 8px;
transition: background 0.1s ease;
}
.ctx-item:hover {
background: rgba(255, 255, 255, 0.12);
color: #ffffff;
text-shadow: var(--glow-subtle);
}
.ctx-item i {
width: 14px;
font-size: 11px;
}
.ctx-sep {
height: 1px;
background: rgba(255, 255, 255, 0.1);
margin: 4px 0;
}
/* ── Mobile Navigation Bar ───────────────────────────────────────────────── */
.mobile-nav {
display: none;
}
/* ── Responsive Mobile & Tablet Experience ──────────────────────────────── */
@media (max-width: 900px) {
:root {
--taskbar-h: 44px;
}
.desktop {
position: relative !important;
overflow-y: auto !important;
overflow-x: hidden !important;
padding: 6px 12px 76px 12px !important;
height: calc(100vh - var(--taskbar-h)) !important;
-webkit-overflow-scrolling: touch;
}
/* Sticky Mobile Quick App Dock */
.mobile-nav {
display: flex !important;
position: sticky;
top: 0;
z-index: 80;
gap: 8px;
padding: 10px 0;
margin-bottom: 12px;
overflow-x: auto;
white-space: nowrap;
background: rgba(0, 0, 0, 0.8);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border-bottom: 1px solid rgba(255, 255, 255, 0.12);
scrollbar-width: none;
}
.mobile-nav::-webkit-scrollbar {
display: none;
}
.mob-nav-btn {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 12px;
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.18);
color: #dcdcdc;
font-family: 'Space Mono', monospace;
font-size: 11px;
cursor: pointer;
flex-shrink: 0;
transition: all 0.15s ease;
}
.mob-nav-btn i {
font-size: 10px;
opacity: 0.75;
}
.mob-nav-btn:active, .mob-nav-btn:hover {
background: rgba(255, 255, 255, 0.2);
border-color: #ffffff;
color: #ffffff;
box-shadow: 0 0 10px rgba(255, 255, 255, 0.4);
}
.window {
position: static !important;
width: 100% !important;
max-width: 100% !important;
height: auto !important;
margin-bottom: 16px !important;
transform: none !important;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.7) !important;
}
.titlebar {
cursor: default !important;
height: 32px;
padding: 0 12px;
}
.window-body {
padding: 14px 12px !important;
}
.link-item {
padding: 10px 12px !important;
min-height: 46px !important;
}
.link-item:active {
background: rgba(255, 255, 255, 0.18) !important;
transform: scale(0.99);
}
/* Mobile visualizer height */
.viz-container {
height: 250px !important;
min-height: 250px !important;
}
/* Mobile terminal optimization */
.terminal-body {
max-height: 440px !important;
overflow-x: auto !important;
-webkit-overflow-scrolling: touch;
}
/* Mobile Taskbar */
.taskbar {
height: 44px;
padding: 0 12px;
}
.taskbar-windows, .taskbar-divider {
display: none !important;
}
.os-logo-btn {
padding: 5px 12px;
font-size: 12px;
}
.music-bar {
gap: 10px;
padding: 3px 8px;
}
.player-btn {
width: 28px !important;
height: 28px !important;
font-size: 11px !important;
}
.volume-slider {
display: none !important;
}
.music-track {
display: inline-block !important;
max-width: 110px;
font-size: 9.5px;
}
.tray-time {
font-size: 11px;
}
}
</style>
</head>
<body>
<!-- Minimal Boot Screen (Click to Start Audio & Video) -->
<div id="loadingScreen" onclick="enterKatOS()">
<div class="boot-minimal">
<div class="boot-logo">katOS</div>
<div class="boot-sub">[ click to enter ]</div>
</div>
</div>
<!-- Dynamic Ambient & Cybernetic Background Layers -->
<video class="video-bg" id="bgVideo" autoplay muted loop playsinline></video>
<canvas id="bgCanvas"></canvas>
<div class="crt-overlay"></div>
<div id="katToast" class="kat-toast"></div>
<!-- Desktop Workspace -->
<div class="desktop" id="desktop">
<!-- Mobile Quick Navigation Bar (Sticky App Dock on Phones) -->
<nav class="mobile-nav" id="mobileNav" aria-label="Mobile Navigation">
<button class="mob-nav-btn" onclick="scrollToWin('win-about')"><i class="fas fa-file-lines"></i> about</button>
<button class="mob-nav-btn" onclick="scrollToWin('win-websites')"><i class="fas fa-globe"></i> websites</button>
<button class="mob-nav-btn" onclick="scrollToWin('win-links')"><i class="fas fa-folder-closed"></i> links</button>
<button class="mob-nav-btn" onclick="scrollToWin('win-skills')"><i class="fas fa-terminal"></i> skillz</button>
<button class="mob-nav-btn" onclick="scrollToWin('win-term')"><i class="fas fa-laptop-code"></i> terminal</button>
<button class="mob-nav-btn" onclick="scrollToWin('win-viz')"><i class="fas fa-wave-square"></i> visualizer</button>
</nav>
<!-- About Window -->
<div class="window active" id="win-about">
<div class="titlebar">
<div class="title-left">
<i class="fas fa-file-lines"></i>
<span>about.md</span>
</div>
<div class="window-controls">
<div class="win-dot-close" onclick="closeWindow('win-about')" title="Close"></div>
</div>
</div>
<div class="window-body">
<p style="color:#ffffff; font-weight:500; margin-bottom: 12px;">yo sup i am the sillest internet goof</p>
<p style="color:#aaa; margin-bottom: 12px;">minecraft shyt, networking experiments, and linux customization.</p>
<p style="color:#555; margin-bottom: 12px; font-family:monospace;">i was on the ███████████████</p>
<p style="color:#aaa; font-size:11px; margin-bottom: 12px; word-break: break-all; font-family: monospace;">yo slide me some rq eth: <span style="color:#fff;">0xB77826c34B8FdfD7F8bD600F0F09Fe20658FA01f</span> btc: <span style="color:#fff;">bc1qnu8s3awtjxpcewc72y7j6cc38pew0c07ngjfwk</span></p>
<p style="color:#bbb; margin-bottom: 12px;">i use arch yea</p>
<p style="color:#ffffff; font-weight:600; text-shadow:var(--glow-subtle);">you da real kat 🤍.</p>
</div>
</div>
<!-- Skills Window -->
<div class="window" id="win-skills">
<div class="titlebar">
<div class="title-left">
<i class="fas fa-terminal"></i>
<span>skillz.bash</span>
</div>
<div class="window-controls">
<div class="win-dot-close" onclick="closeWindow('win-skills')" title="Close"></div>
</div>
</div>
<div class="window-body">
<div style="margin-bottom: 8px; color: var(--text-dim);"># system competencies:</div>
<span class="skill-tag"><i class="fab fa-linux"></i> linux / arch</span>
<span class="skill-tag"><i class="fas fa-cube"></i> minecraft servers</span>
<span class="skill-tag"><i class="fas fa-server"></i> self hosting</span>
<span class="skill-tag"><i class="fas fa-network-wired"></i> server networking</span>
<span class="skill-tag"><i class="fab fa-java"></i> java / plugins</span>
<span class="skill-tag"><i class="fab fa-rust"></i> learning rust</span>
<div style="margin: 14px 0 6px; color: var(--text-dim);"># personality traits:</div>
<span class="skill-tag">yappin</span>
<span class="skill-tag">im crine</span>
<span class="skill-tag">tweaking</span>
<span class="skill-tag">hahahahahha</span>
<span class="skill-tag">cool content</span>
<span class="skill-tag">very funny</span>
<span class="skill-tag"><i class="fas fa-cat"></i> veri fond of cats</span>
</div>
</div>
<!-- Websites & Projects Window -->
<div class="window" id="win-websites">
<div class="titlebar">
<div class="title-left">
<i class="fas fa-globe"></i>
<span>websites.html</span>
</div>
<div class="window-controls">
<div class="win-dot-close" onclick="closeWindow('win-websites')" title="Close"></div>
</div>
</div>
<div class="window-body">
<a class="link-item" href="https://katdrop.oiupoyt.space" target="_blank" rel="noopener">
<i class="fas fa-file-arrow-up"></i>
<div>
<div style="color:#fff; font-weight:500;">katdrop.oiupoyt.space</div>
<div style="font-size:10px; color:#777;">minimal 10-min file transfer</div>
</div>
<span class="badge">live</span>
</a>
<a class="link-item" href="https://kats.oiupoyt.space" target="_blank" rel="noopener">
<i class="fas fa-cat"></i>
<div>
<div style="color:#fff; font-weight:500;">kats.oiupoyt.space</div>
<div style="font-size:10px; color:#777;">look at cat. feel good.</div>
</div>
<span class="badge">live</span>
</a>
<a class="link-item" href="https://jd.oiupoyt.space" target="_blank" rel="noopener">
<i class="fas fa-download"></i>
<div>
<div style="color:#fff; font-weight:500;">jd.oiupoyt.space</div>
<div style="font-size:10px; color:#777;">just download yt &amp; ig media</div>
</div>
<span class="badge">live</span>
</a>
<a class="link-item" href="https://toolbox.oiupoyt.space" target="_blank" rel="noopener">
<i class="fas fa-wrench"></i>
<div>
<div style="color:#fff; font-weight:500;">toolbox.oiupoyt.space</div>
<div style="font-size:10px; color:#777;">client-side dev utility suite</div>
</div>
<span class="badge">live</span>
</a>
<a class="link-item" href="https://katproxy.oiupoyt.space" target="_blank" rel="noopener">
<i class="fas fa-server"></i>
<div>
<div style="color:#fff; font-weight:500;">katproxy.oiupoyt.space</div>
<div style="font-size:10px; color:#777;">minimal proxy list &amp; latency prober</div>
</div>
<span class="badge">live</span>
</a>
</div>
</div>
<!-- Social Links Window -->
<div class="window" id="win-links">
<div class="titlebar">
<div class="title-left">
<i class="fas fa-folder-closed"></i>
<span>links.zip</span>
</div>
<div class="window-controls">
<div class="win-dot-close" onclick="closeWindow('win-links')" title="Close"></div>
</div>
</div>
<div class="window-body">
<a class="link-item" href="https://github.com/oiupoyt" target="_blank" rel="noopener">
<i class="fab fa-github"></i>
<span>GitHub</span>
<span class="badge">@oiupoyt</span>
</a>
<a class="link-item" href="https://discordapp.com/users/1082968457559146506" target="_blank" rel="noopener">
<i class="fab fa-discord"></i>
<span>Discord</span>
<span class="badge">@oiupoyt</span>
</a>
<a class="link-item" href="https://www.instagram.com/oiupoyt/" target="_blank" rel="noopener">
<i class="fab fa-instagram"></i>
<span>Instagram</span>
<span class="badge">@oiupoyt</span>
</a>
<a class="link-item" href="https://www.youtube.com/@OIUPOYT" target="_blank" rel="noopener">
<i class="fab fa-youtube"></i>
<span>YouTube</span>
<span class="badge">@OIUPOYT</span>
</a>
<a class="link-item" href="https://www.roblox.com/users/1529210346/profile" target="_blank" rel="noopener">
<i class="fas fa-gamepad"></i>
<span>Roblox</span>
<span class="badge">oiupoyt</span>
</a>
<a class="link-item" href="https://modrinth.com/user/oiupoyt" target="_blank" rel="noopener">
<i class="fas fa-cube"></i>
<span>Modrinth</span>
<span class="badge">mods</span>
</a>
</div>
</div>
<!-- Interactive FastFetch Terminal Window -->
<div class="window active" id="win-term">
<div class="titlebar">
<div class="title-left">
<i class="fas fa-terminal"></i>
<span>~fastfetch</span>
</div>
<div class="window-controls">
<div class="win-dot-close" onclick="closeWindow('win-term')" title="Close"></div>
</div>
</div>
<div class="terminal-body" id="termBody" onclick="focusTermInput()">
<div class="fastfetch-pre" id="fastfetchArt"></div>
<div id="termHistory"></div>
<div class="term-prompt-line">
<span class="term-prompt">oiupoyt@archlinux:~$</span>
<input type="text" class="term-input" id="termInput" autocomplete="off" spellcheck="false" autofocus>
</div>
</div>
</div>
<!-- Audio Visualizer Window -->
<div class="window" id="win-viz">
<div class="titlebar">
<div class="title-left">
<i class="fas fa-wave-square"></i>
<span>visualizer</span>
</div>
<div class="window-controls">
<div class="win-dot-close" onclick="closeWindow('win-viz')" title="Close"></div>
</div>
</div>
<div class="viz-container" id="vizBox">
<canvas id="vizCanvas" title="Click to cycle visualizer mode"></canvas>
<div class="viz-overlay">
<span>mode:</span>
<span class="viz-mode-badge" id="vizModeName">mirror pulse</span>
</div>
<div class="viz-hint">[click visualizer to switch mode]</div>
<span class="viz-idle-msg" id="vizIdleMsg">play music to visualize</span>
</div>
</div>
</div>
<!-- Taskbar -->
<footer class="taskbar">
<div class="taskbar-left">
<div class="os-logo-btn" onclick="handleOsLogoClick()" title="katOS Launcher / Scroll to Top">
<span>katOS</span>
</div>
<div class="taskbar-divider"></div>
<div class="taskbar-windows" id="taskbarWinContainer"></div>
</div>
<div class="taskbar-right">
<!-- Music Player Controls -->
<div class="music-bar">
<i class="fas fa-compact-disc music-icon paused" id="musicDisc"></i>
<span class="music-track" id="musicTrack">Beat 44</span>
<button class="player-btn" id="playBtn" onclick="togglePlay()" title="Play/Pause">
<i class="fas fa-play" id="playIcon"></i>
</button>
<input type="range" class="volume-slider" id="volSlider" min="0" max="100" value="45" title="Volume">
<button class="player-btn" onclick="cycleVizMode()" title="Switch Visualizer Mode">
<i class="fas fa-sliders"></i>
</button>
</div>
<!-- System Tray Clock (RAM removed) -->
<span class="tray-time" id="clock">00:00:00</span>
</div>
</footer>
<!-- Desktop Right-Click Context Menu -->
<div class="context-menu" id="ctxMenu">
<div class="ctx-item" onclick="spawnFastfetch()"><i class="fas fa-terminal"></i> Open Terminal</div>
<div class="ctx-item" onclick="cycleBgMode()"><i class="fas fa-layer-group"></i> Switch Background Mode</div>
<div class="ctx-sep"></div>
<div class="ctx-item" onclick="togglePlay()"><i class="fas fa-music"></i> Play / Pause Music</div>
<div class="ctx-item" onclick="restoreAllWindows()"><i class="fas fa-window-restore"></i> Show All Windows</div>
<div class="ctx-item" onclick="autoSpaceWindows()"><i class="fas fa-rotate-left"></i> Reset &amp; Space Windows</div>
</div>
<!-- Audio Element -->
<audio id="bgMusic" loop preload="auto">
<source src="music.mp3" type="audio/mpeg">
</audio>
<script>
// ── Global System State ──────────────────────────────────────────────────
let topZIndex = 40;
const windowRegistry = ['win-about', 'win-skills', 'win-websites', 'win-links', 'win-term', 'win-viz'];
// ── Auto-Space Terminals on Start (Organic Staggered Layout) ───────────
function autoSpaceWindows() {
const W = window.innerWidth;
const H = window.innerHeight - 36; // leave room for taskbar
if (W >= 1500) {
// Broad monitor: spread out organically across entire screen width
setWinPos('win-about', 45, 60, 350, null);
setWinPos('win-skills', 380, 110, 360, null);
setWinPos('win-websites', 85, 510, 355, null);
setWinPos('win-links', 435, 560, 340, null);
setWinPos('win-term', 40, 955, Math.min(620, W - 990), 378);
setWinPos('win-viz', 450, 1060, Math.min(680, W - 1080), 305);
} else if (W >= 1150) {
// Standard laptop / desktop: spread out with natural asymmetry & zero overlap
setWinPos('win-about', 28, 32, 335, null);
setWinPos('win-skills', 345, 55, 345, null);
setWinPos('win-websites', 48, 418, 340, null);
setWinPos('win-links', 365, 438, 330, null);
setWinPos('win-term', 22, 785, Math.min(560, W - 805), 378);
setWinPos('win-viz', 420, 790, Math.min(620, W - 810), 285);
} else if (W >= 800) {
// Tablet / compact
setWinPos('win-about', 20, 24, 335, null);
setWinPos('win-skills', 325, 45, 345, null);
setWinPos('win-websites', 36, 395, 335, null);
setWinPos('win-links', 355, 420, 325, null);
setWinPos('win-term', 685, 20, W - 44, 378);
setWinPos('win-viz', 1085, 35, W - 70, 280);
} else {
// Mobile (<800px): clear inline pixel coordinates so responsive CSS applies cleanly
windowRegistry.forEach(id => {
const win = document.getElementById(id);
if (win) {
win.style.top = '';
win.style.left = '';
win.style.width = '';
win.style.height = '';
}
});
}
restoreAllWindows();
bringToFront(document.getElementById('win-term'));
}
// ── Mobile App Navigation Quick Jump ─────────────────────────────────────
function scrollToWin(id) {
const win = document.getElementById(id);
if (!win) return;
if (win.classList.contains('minimized')) {
win.classList.remove('minimized');
}
bringToFront(win);
if (window.innerWidth <= 900) {
const desktop = document.getElementById('desktop');
if (desktop) {
const winTop = win.offsetTop - 54;
desktop.scrollTo({ top: Math.max(0, winTop), behavior: 'smooth' });
}
}
}
// ── Taskbar OS Logo Action (Mobile: Scroll to Top, Desktop: Reset Windows) ─
function handleOsLogoClick() {
if (window.innerWidth <= 900) {
const desktop = document.getElementById('desktop');
if (desktop) {
desktop.scrollTo({ top: 0, behavior: 'smooth' });
}
} else {
autoSpaceWindows();
showToast('[ katOS: Windows Reset & Centered ]');
}
}
function setWinPos(id, top, left, width, height) {
const win = document.getElementById(id);
if (!win) return;
win.style.top = top + 'px';
win.style.left = left + 'px';
if (width) win.style.width = width + 'px';
if (height) {
win.style.height = height + 'px';
} else {
win.style.height = 'auto';
}
}
// ── Window Dragging & State ──────────────────────────────────────────────
function bringToFront(win) {
if (!win) return;
topZIndex++;
win.style.zIndex = topZIndex;
document.querySelectorAll('.window').forEach(w => w.classList.remove('active'));
win.classList.add('active');
updateTaskbar();
}
function initWindows() {
document.querySelectorAll('.window').forEach(win => {
const bar = win.querySelector('.titlebar');
if (!bar) return;
// Focus on click
win.addEventListener('mousedown', () => bringToFront(win));
// Dragging
bar.addEventListener('mousedown', e => {
if (e.target.closest('.win-dot-close')) return;
bringToFront(win);
if (win.classList.contains('maximized')) return;
const rect = win.getBoundingClientRect();
const shiftX = e.clientX - rect.left;
const shiftY = e.clientY - rect.top;
win.style.willChange = 'left, top';
let dragRaf = null;
function onMouseMove(moveEvent) {
const newX = Math.max(0, Math.min(window.innerWidth - 60, moveEvent.clientX - shiftX));
const newY = Math.max(0, Math.min(window.innerHeight - 70, moveEvent.clientY - shiftY));
if (!dragRaf) {
dragRaf = requestAnimationFrame(() => {
win.style.left = newX + 'px';
win.style.top = newY + 'px';
dragRaf = null;
});
}
}
function onMouseUp() {
if (dragRaf) {
cancelAnimationFrame(dragRaf);
dragRaf = null;
}
win.style.willChange = 'auto';
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
if (win.id === 'win-viz' && typeof resizeVizCanvas === 'function') resizeVizCanvas();
}
document.addEventListener('mousemove', onMouseMove, { passive: true });
document.addEventListener('mouseup', onMouseUp);
});
});
autoSpaceWindows();
updateTaskbar();
}
function closeWindow(id) {
const win = document.getElementById(id);
if (!win) return;
win.classList.add('minimized');
updateTaskbar();
}
function restoreWindow(id) {
const win = document.getElementById(id);
if (!win) return;
if (win.classList.contains('minimized')) {
win.classList.remove('minimized');
bringToFront(win);
} else if (win.classList.contains('active')) {
win.classList.add('minimized');
} else {
bringToFront(win);
}
updateTaskbar();
}
function restoreAllWindows() {
windowRegistry.forEach(id => {
const win = document.getElementById(id);
if (win) {
win.classList.remove('minimized');
win.classList.remove('maximized');
}
});
updateTaskbar();
}
function updateTaskbar() {
const container = document.getElementById('taskbarWinContainer');
if (!container) return;
container.innerHTML = '';
windowRegistry.forEach(id => {
const win = document.getElementById(id);
if (!win) return;
const titleEl = win.querySelector('.title-left span');
const iconEl = win.querySelector('.title-left i');
const titleText = titleEl ? titleEl.textContent : id;
const iconClass = iconEl ? iconEl.className : 'fas fa-terminal';
const btn = document.createElement('div');
btn.className = 'taskbar-win-btn' + (win.classList.contains('active') && !win.classList.contains('minimized') ? ' active' : '');
btn.innerHTML = `<i class="${iconClass}"></i><span>${titleText}</span>`;
btn.onclick = () => restoreWindow(id);
container.appendChild(btn);
});
}
// ── FastFetch ASCII Art & Specs (Monochrome Glow) ─────────────────────────
const fastfetchArtEl = document.getElementById('fastfetchArt');
const fa = cls => `<i class="${cls}" style="width:1.2em;text-align:center;display:inline-block;opacity:0.75;font-size:10px;"></i>`;
const fastfetchLinesDesktop = [
` ▄`,
` ▟█▙`,
` ▟███▙ <b style="color:#ffffff; text-shadow:var(--glow-subtle);">oiupoyt</b><span style="opacity:0.4">@</span><b style="color:#ffffff;">archlinux</b>`,
` ▟█████▙ <span style="opacity:0.35">────────────────────────</span>`,
` ▟███████▙ ${fa('fas fa-laptop')} <span style="opacity:0.45;font-size:10px">OS</span> Arch Linux x86_64`,
` ▂▔▀▜██████▙ ${fa('fas fa-microchip')} <span style="opacity:0.45;font-size:10px">Kernel</span> Linux 6.19.8-arch1-1`,
` ▟██▅▂▝▜█████▙ ${fa('fas fa-box-open')} <span style="opacity:0.45;font-size:10px">Packages</span> 1254 (pacman), 60 (flatpak)`,
` ▟█████████████▙ ${fa('fas fa-terminal')} <span style="opacity:0.45;font-size:10px">Shell</span> fish 4.5.0`,
` ▟███████████████▙ ${fa('fas fa-display')} <span style="opacity:0.45;font-size:10px">Terminal</span> kitty 0.46.1`,
` ▟█████████████████▙ ${fa('fas fa-window-restore')} <span style="opacity:0.45;font-size:10px">WM</span> Hyprland 0.54.2 (Wayland)`,
` ▟███████████████████▙ ${fa('fas fa-palette')} <span style="opacity:0.45;font-size:10px">Theme</span> monochrome-dark`,
` ▟█████████▛▀▀▜████████▙ ${fa('fas fa-font')} <span style="opacity:0.45;font-size:10px">Font</span> Space Mono (11pt)`,
` ▟████████▛ ▜███████▙ ${fa('fas fa-clock')} <span style="opacity:0.45;font-size:10px">Uptime</span> <span id="dynUptime">12 hours, 42 mins</span>`,
` ▟█████████ ████████▙ ${fa('fas fa-music')} <span style="opacity:0.45;font-size:10px">Music</span> Beat 44`,
` ▟██████████ █████▆▅▄▃▂ ${fa('fas fa-cat')} <span style="opacity:0.45;font-size:10px">Status</span> you da real kat 🤍`,
` ▟██████████▛ ▜█████████▙ ${fa('fas fa-network-wired')} <span style="opacity:0.45;font-size:10px">Websites</span> katdrop • kats • jd • toolbox • katproxy`,
` ▟██████▀▀▀ ▀▀██████▙`,
` ▟███▀▘ ▝▀███▙`,
` ▟▛▀ ▀▜▙`,
];
function renderFastfetch() {
if (!fastfetchArtEl) return;
if (window.innerWidth <= 640) {
const logoAscii = [
` ▟█▙`,
` ▟███▙`,
` ▟█████▙`,
` ▟███████▙`,
` ▂▔▀▜██████▙`,
` ▟██▅▂▝▜█████▙`,
` ▟█████████████▙`,
`▟██████▀▀ ▀▀████▙`
].map(l => `<div class="term-row" style="color:#ffffff;text-shadow:var(--glow-subtle);">${l}</div>`).join('');
const specItems = [
`<b style="color:#ffffff; text-shadow:var(--glow-subtle);">oiupoyt</b><span style="opacity:0.4">@</span><b style="color:#ffffff;">archlinux</b>`,
`<span style="opacity:0.35">────────────────────────</span>`,
`${fa('fas fa-laptop')} <span style="opacity:0.45;font-size:10px">OS</span> Arch Linux x86_64`,
`${fa('fas fa-microchip')} <span style="opacity:0.45;font-size:10px">Kernel</span> Linux 6.19.8-arch1-1`,
`${fa('fas fa-box-open')} <span style="opacity:0.45;font-size:10px">Packages</span> 1254 (pacman), 60 (flatpak)`,
`${fa('fas fa-terminal')} <span style="opacity:0.45;font-size:10px">Shell</span> fish 4.5.0`,
`${fa('fas fa-display')} <span style="opacity:0.45;font-size:10px">Terminal</span> kitty 0.46.1`,
`${fa('fas fa-window-restore')} <span style="opacity:0.45;font-size:10px">WM</span> Hyprland 0.54.2`,
`${fa('fas fa-palette')} <span style="opacity:0.45;font-size:10px">Theme</span> monochrome-dark`,
`${fa('fas fa-font')} <span style="opacity:0.45;font-size:10px">Font</span> Space Mono (11pt)`,
`${fa('fas fa-clock')} <span style="opacity:0.45;font-size:10px">Uptime</span> <span id="dynUptime">12 hours, 42 mins</span>`,
`${fa('fas fa-music')} <span style="opacity:0.45;font-size:10px">Music</span> Beat 44`,
`${fa('fas fa-cat')} <span style="opacity:0.45;font-size:10px">Status</span> you da real kat 🤍`,
`${fa('fas fa-network-wired')} <span style="opacity:0.45;font-size:10px">Websites</span> katdrop • kats • jd • toolbox • katproxy`
].map(s => `<div class="term-row">${s}</div>`).join('');
fastfetchArtEl.innerHTML = `<div style="margin-bottom:8px;text-align:center;">${logoAscii}</div><div>${specItems}</div>`;
} else {
fastfetchArtEl.innerHTML = fastfetchLinesDesktop.map(l => `<div class="term-row">${l}</div>`).join('');
}
}
renderFastfetch();
// ── Interactive Terminal Shell ───────────────────────────────────────────
const termInput = document.getElementById('termInput');
const termHistory = document.getElementById('termHistory');
const termBody = document.getElementById('termBody');
let cmdHistory = [];
let historyIdx = -1;
function focusTermInput() {
termInput.focus();
}
termInput.addEventListener('keydown', e => {
if (e.key === 'Enter') {
const cmd = termInput.value.trim();
if (cmd) {
if (cmdHistory.length > 60) cmdHistory.shift();
cmdHistory.push(cmd);
historyIdx = cmdHistory.length;
execCommand(cmd);
}
termInput.value = '';
termBody.scrollTop = termBody.scrollHeight;
} else if (e.key === 'ArrowUp') {
if (historyIdx > 0) {
historyIdx--;
termInput.value = cmdHistory[historyIdx];
}
} else if (e.key === 'ArrowDown') {
if (historyIdx < cmdHistory.length - 1) {
historyIdx++;
termInput.value = cmdHistory[historyIdx];
} else {
historyIdx = cmdHistory.length;
termInput.value = '';
}
} else if (e.key === 'Tab') {
e.preventDefault();
const cur = termInput.value.trim().toLowerCase();
const available = ['help', 'fastfetch', 'neofetch', 'clear', 'cat', 'cowsay', 'fortune', 'sl', 'cmatrix', 'ls', 'projects', 'skills', 'music', 'matrix', 'whoami', 'date', 'reboot'];
const match = available.find(c => c.startsWith(cur));
if (match) termInput.value = match;
}
});
// ── Linux Fortunes & Cowsay Generator ────────────────────────────────────
const katFortunes = [
"Arch Linux: because installing an OS in 40 manual commands builds character.",
"A cat has 9 lives. A Linux kernel panic has infinite retries.",
"There are 10 types of people: those who understand binary, and cats who sit on keyboards.",
"Minecraft server status: running out of RAM. Solution: fed the cat instead.",
"Hyprland config broke at 3 AM. Worth it for the 1px obsidian border.",
"sudo rm -rf / --no-preserve-root : The cat walked across the keyboard again.",
"Fastfetch is not just a tool; it's an obsidian lifestyle.",
"Real hackers don't sleep, they just wait for pacman -Syu to finish.",
"Self-hosting everything because trusting the cloud is a vulnerability.",
"Your CPU is currently 99.8% cooler because katOS is running.",
"yo sup i am the sillest internet goof"
];
function getRandomFortune() {
return katFortunes[Math.floor(Math.random() * katFortunes.length)];
}
function formatCowsay(msg) {
const cleanMsg = (msg || 'meow').replace(/[\r\n]+/g, ' ').trim();
const maxLen = 40;
const wrapped = [];
let words = cleanMsg.split(' ');
let curLine = '';
for (let w of words) {
if ((curLine + ' ' + w).trim().length > maxLen) {
if (curLine) wrapped.push(curLine);
curLine = w;
} else {
curLine = (curLine ? curLine + ' ' : '') + w;
}
}
if (curLine) wrapped.push(curLine);
if (wrapped.length === 0) wrapped.push('meow');
const width = Math.max(...wrapped.map(l => l.length));
const borderTop = ' ' + '_'.repeat(width + 2);
const borderBot = ' ' + '-'.repeat(width + 2);
const body = wrapped.map((line, idx) => {
const pad = ' '.repeat(width - line.length);
if (wrapped.length === 1) return `< ${line} >`;
if (idx === 0) return `/ ${line}${pad} \\`;
if (idx === wrapped.length - 1) return `\\ ${line}${pad} /`;
return `| ${line}${pad} |`;
}).join('\n');
return `${borderTop}\n${body}\n${borderBot}\n \\ ^__^\n \\ (oo)\\_______\n (__)\\ )\\/\\\n ||----w |\n || ||`;
}
// ── Classic "sl" Steam Locomotive Easter Egg ──────────────────────────────
function runSteamLocomotive() {
const trainContainer = document.createElement('pre');
trainContainer.className = 'sl-train';
trainContainer.style.cssText = 'font-family:monospace; line-height:1.15; color:#ffffff; text-shadow:var(--glow-subtle); white-space:pre; margin:8px 0; overflow:hidden; font-size:10px;';
termHistory.appendChild(trainContainer);
termInput.disabled = true;
const trainArt = [
` ( ) (@@) ( ) (@) () @@ O`,
` (@@@)`,
` ( )`,
` (@@@@)`,
` ( )`,
` ==== ________ ___________`,
` _D _| |_______/ \\__I_I_____===__|_________|`,
` |(_)--- | H\\________/ | | =|___ ___|`,
` / | | H | | | | | |`,
` | | | H |__--------------------| [katOS] |`,
` | ________|___H__/__|_____/[ ] \\___|____|_________|`,
` (O)____(O) [ ] (O)(O) (O) (O) (O)(O)`
];
let offset = 42;
const interval = setInterval(() => {
if (offset < -68) {
clearInterval(interval);
trainContainer.remove();
termInput.disabled = false;
termInput.focus();
const doneRow = document.createElement('div');
doneRow.className = 'term-output';
doneRow.innerHTML = `<span style="color:#ffffff; text-shadow:var(--glow-subtle);"><i class="fas fa-train"></i> sl: Steam Locomotive chugged past oiupoyt@archlinux!</span>`;
termHistory.appendChild(doneRow);
termBody.scrollTop = termBody.scrollHeight;
return;
}
const indent = Math.max(0, offset);
const padding = ' '.repeat(indent);
const sliceAmt = offset < 0 ? Math.abs(offset) : 0;
trainContainer.textContent = trainArt
.map(line => padding + (sliceAmt > 0 ? line.slice(sliceAmt) : line))
.join('\n');
offset -= 3;
termBody.scrollTop = termBody.scrollHeight;
}, 50);
}
function execCommand(cmdLine) {
const parts = cmdLine.split(' ');
const cmd = parts[0].toLowerCase();
const arg = parts.slice(1).join(' ');
// Prune old terminal nodes to keep DOM lightweight
while (termHistory.children.length > 60) {
termHistory.removeChild(termHistory.firstChild);
}
const row = document.createElement('div');
row.innerHTML = `<span class="term-prompt">oiupoyt@archlinux:~$</span> <span class="term-cmd">${escapeHtml(cmdLine)}</span>`;
termHistory.appendChild(row);
const out = document.createElement('div');
out.className = 'term-output';
switch(cmd) {
case 'help':
out.innerHTML = `Available commands:
<b style="color:#ffffff">fastfetch</b> - display Arch Linux logo & system specs
<b style="color:#ffffff">cowsay &lt;msg&gt;</b> - ASCII cow speaks your text (or fortune)
<b style="color:#ffffff">fortune</b> - random funny Linux, Arch, or cat fortune
<b style="color:#ffffff">sl</b> - classic steam locomotive runs across terminal
<b style="color:#ffffff">cmatrix</b> - stream falling KatOS digital rain
<b style="color:#ffffff">ls</b> - list virtual directory contents
<b style="color:#ffffff">cat &lt;file&gt;</b> - display contents of a file (e.g. cat about.md)
<b style="color:#ffffff">projects</b> - list active web projects & links
<b style="color:#ffffff">skills</b> - show competencies & skills
<b style="color:#ffffff">music</b> - music play | pause | vol &lt;0-100&gt;
<b style="color:#ffffff">bg &lt;mode&gt;</b> - change background (liquid, horizon, matrix, video)
<b style="color:#ffffff">catpic</b> - fetch a fresh cat image
<b style="color:#ffffff">whoami</b> - print current session bio
<b style="color:#ffffff">donate</b> - crypto donation addresses (eth / btc)
<b style="color:#ffffff">clear</b> - clear terminal screen
<b style="color:#ffffff">date</b> - show current date and time
<b style="color:#ffffff">reboot</b> - restart katOS desktop`;
break;
case 'fastfetch':
case 'neofetch':
fastfetchArtEl.style.display = 'block';
renderFastfetch();
out.textContent = '';
break;
case 'cowsay':
const cowMsg = arg.trim() || getRandomFortune();
out.innerHTML = `<pre style="font-family:monospace;margin:0;color:#fff;text-shadow:var(--glow-subtle);">${escapeHtml(formatCowsay(cowMsg))}</pre>`;
break;
case 'fortune':
const fort = getRandomFortune();
out.innerHTML = `<span style="color:#ffffff;text-shadow:var(--glow-subtle);font-style:italic;">"${escapeHtml(fort)}"</span>`;
break;
case 'sl':
runSteamLocomotive();
return;
case 'cmatrix':
setBgMode('matrix-rain');
out.innerHTML = `<span style="color:#00ff66; text-shadow:0 0 8px rgba(0,255,100,0.8); font-weight:bold;">[ CMATRIX PROTOCOL ENGAGED ]</span>\n"Wake up, oiupoyt... The matrix has you."\nKatOS digital rain is now cascading.\nType <b style="color:#ffffff">bg liquid</b> to return to Liquid Void.`;
break;
case 'ls':
out.innerHTML = `<span style="color:#ffffff">about.md</span> <span style="color:#ffffff">skillz.bash</span> <span style="color:#ffffff">websites.html</span> <span style="color:#ffffff">links.zip</span> <span style="color:#888">music.mp3</span> <span style="color:#888">backgroundVideo.mp4</span>`;
break;
case 'cat':
if (!arg) {
out.textContent = 'cat: missing file operand. try: cat about.md (or: catpic)';
} else if (arg.includes('about')) {
out.textContent = `yo sup i am the sillest internet goof\nminecraft shyt, networking experiments, and linux customization.\ni was on the ███████████████\nyo slide me some rq eth: 0xB77826c34B8FdfD7F8bD600F0F09Fe20658FA01f btc: bc1qnu8s3awtjxpcewc72y7j6cc38pew0c07ngjfwk\ni use arch yea\nyou da real kat 🤍.`;
} else if (arg.includes('skill')) {
out.textContent = `linux / arch • Minecraft Servers • Self Hosting • server networking • Java / Plugins • learning rust • yappin • im crine • tweaking • hahahahahha • cool content • very funny • veri fond of cats`;
} else if (arg.includes('website')) {
out.innerHTML = `• katdrop: https://katdrop.oiupoyt.space\n• kats: https://kats.oiupoyt.space\n• jd: https://jd.oiupoyt.space\n• toolbox: https://toolbox.oiupoyt.space\n• katproxy: https://katproxy.oiupoyt.space`;
} else if (arg.includes('link')) {
out.innerHTML = `• GitHub: https://github.com/oiupoyt\n• Discord: @oiupoyt\n• Instagram: @oiupoyt\n• YouTube: @OIUPOYT\n• Modrinth: oiupoyt\n• Roblox: oiupoyt`;
} else {
out.textContent = `cat: ${arg}: Permission denied or file is binary`;
}
break;
case 'donate':
case 'crypto':
out.innerHTML = `yo slide me some rq:\n• eth: <span style="color:#ffffff;">0xB77826c34B8FdfD7F8bD600F0F09Fe20658FA01f</span>\n• btc: <span style="color:#ffffff;">bc1qnu8s3awtjxpcewc72y7j6cc38pew0c07ngjfwk</span>`;
break;
case 'projects':
out.innerHTML = `oiupoyt projects:
1. <a href="https://katdrop.oiupoyt.space" target="_blank" style="color:#ffffff; text-decoration:underline;">katdrop.oiupoyt.space</a> - minimal 10-min file transfer service
2. <a href="https://kats.oiupoyt.space" target="_blank" style="color:#ffffff; text-decoration:underline;">kats.oiupoyt.space</a> - look at cat. feel good.
3. <a href="https://jd.oiupoyt.space" target="_blank" style="color:#ffffff; text-decoration:underline;">jd.oiupoyt.space</a> - youtube & instagram media downloader
4. <a href="https://toolbox.oiupoyt.space" target="_blank" style="color:#ffffff; text-decoration:underline;">toolbox.oiupoyt.space</a> - client-side developer utility suite
5. <a href="https://katproxy.oiupoyt.space" target="_blank" style="color:#ffffff; text-decoration:underline;">katproxy.oiupoyt.space</a> - minimal proxy list & latency prober`;
break;
case 'skills':
out.textContent = 'linux / arch, minecraft servers, self hosting, server networking, java / plugins, yappin, im crine, tweaking, cats';
break;
case 'music':
if (arg === 'play') {
playMusic();
out.textContent = 'audio: playing';
} else if (arg === 'pause') {
pauseMusic();
out.textContent = 'audio: paused';
} else if (arg.startsWith('vol')) {
const val = parseInt(arg.split(' ')[1]);
if (!isNaN(val) && val >= 0 && val <= 100) {
setVolume(val);
out.textContent = `audio: volume set to ${val}%`;
} else {
out.textContent = 'usage: music vol <0-100>';
}
} else {
out.textContent = 'usage: music [play | pause | vol <0-100>]';
}
break;
case 'catpic':
case 'cat-image':
out.innerHTML = `Fetching cat...`;
fetch('https://api.thecatapi.com/v1/images/search')
.then(r => r.json())
.then(data => {
if (data && data[0] && data[0].url) {
out.innerHTML = `<img src="${data[0].url}" style="max-width:280px;max-height:200px;border:1px solid #444;margin-top:6px;display:block;"><span style="font-size:10px;color:#888;">meow</span>`;
termBody.scrollTop = termBody.scrollHeight;
}
}).catch(() => {
out.textContent = 'cat error: no cat found :(';
});
break;
case 'bg':
case 'background':
if (arg === 'liquid' || arg === 'void' || arg === 'fluid') {
setBgMode('liquid-void');
out.textContent = 'background set to: Liquid Void Fluid';
} else if (arg === 'cyber' || arg === 'horizon') {
setBgMode('cyber-horizon');
out.textContent = 'background set to: Cyber 3D Horizon';
} else if (arg === 'matrix' || arg === 'rain') {
setBgMode('matrix-rain');
out.textContent = 'background set to: KatOS Digital Rain';
} else if (arg === 'video') {
setBgMode('video-ambient');
out.textContent = 'background set to: Ambient Video';
} else {
cycleBgMode();
out.textContent = `background switched to: ${bgModes[currentBgModeIdx].name}\nusage: bg [liquid | horizon | matrix | video]`;
}
break;
case 'matrix':
setBgMode('matrix-rain');
out.textContent = 'background set to: KatOS Digital Rain';
break;
case 'clear':
termHistory.innerHTML = '';
fastfetchArtEl.style.display = 'none';
return;
case 'whoami':
out.textContent = 'oiupoyt — linux enthusiast, minecraft tinkerer, cat lover';
break;
case 'uname':
case 'uname -a':
out.textContent = 'Linux katOS 6.19.8-arch1-1 #1 SMP PREEMPT_DYNAMIC x86_64 GNU/Linux';
break;
case 'date':
out.textContent = new Date().toString();
break;
case 'reboot':
location.reload();
return;
default:
out.textContent = `bash: ${cmd}: command not found. type 'help' for available commands.`;
}
termHistory.appendChild(out);
termBody.scrollTop = termBody.scrollHeight;
}
function spawnFastfetch() {
restoreWindow('win-term');
fastfetchArtEl.style.display = 'block';
}
function escapeHtml(str) {
return String(str).replace(/[&<>'"]/g, t => ({
'&': '&amp;', '<': '&lt;', '>': '&gt;', "'": '&#39;', '"': '&quot;'
}[t] || t));
}
// ── Audio & Visualizer Engine (Glow White Monochrome) ─────────────────────
const bgMusic = document.getElementById('bgMusic');
const playBtn = document.getElementById('playBtn');
const playIcon = document.getElementById('playIcon');
const musicDisc = document.getElementById('musicDisc');
const volSlider = document.getElementById('volSlider');
const vizCanvas = document.getElementById('vizCanvas');
const vizCtx = vizCanvas.getContext('2d');
const vizIdleMsg = document.getElementById('vizIdleMsg');
const vizModeName = document.getElementById('vizModeName');
let isPlaying = false;
let audioCtx, analyser, source, vizAnimId;
let vizConnected = false;
const vizModes = ['mirror pulse', 'spectrum bars'];
let currentVizModeIdx = 0;
function setupAudio() {
if (vizConnected) return;
try {
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
analyser = audioCtx.createAnalyser();
analyser.fftSize = 256;
analyser.smoothingTimeConstant = 0.82;
source = audioCtx.createMediaElementSource(bgMusic);
source.connect(analyser);
analyser.connect(audioCtx.destination);
vizConnected = true;
} catch (err) {
console.warn('AudioContext init error:', err);
}
}
function playMusic() {
setupAudio();
if (audioCtx && audioCtx.state === 'suspended') {
audioCtx.resume();
}
bgMusic.play().then(() => {
isPlaying = true;
playIcon.className = 'fas fa-pause';
musicDisc.classList.remove('paused');
startVisualizer();
}).catch(err => {
console.warn('Audio play prevented:', err);
});
}
function pauseMusic() {
bgMusic.pause();
isPlaying = false;
playIcon.className = 'fas fa-play';
musicDisc.classList.add('paused');
stopVisualizer();
}
function togglePlay() {
if (isPlaying) {
pauseMusic();
} else {
playMusic();
}
}
function setVolume(val) {
bgMusic.volume = val / 100;
volSlider.value = val;
}
volSlider.addEventListener('input', e => {
setVolume(e.target.value);
});
bgMusic.volume = volSlider.value / 100;
bgMusic.addEventListener('ended', () => {
pauseMusic();
});
// ── Visualizer Rendering (Glow White Spectrum) ────────────────────────────
function cycleVizMode() {
currentVizModeIdx = (currentVizModeIdx + 1) % vizModes.length;
vizModeName.textContent = vizModes[currentVizModeIdx];
}
vizCanvas.addEventListener('click', cycleVizMode);
const sharedFreqData = new Uint8Array(128);
function resizeVizCanvas() {
const rect = vizCanvas.getBoundingClientRect();
const dpr = Math.min(window.devicePixelRatio || 1, 2);
vizCanvas.width = (rect.width || 500) * dpr;
vizCanvas.height = (rect.height || 240) * dpr;
}
function startVisualizer() {
if (!analyser) return;
vizIdleMsg.style.opacity = '0';
resizeVizCanvas();
cancelAnimationFrame(vizAnimId);
drawVisualizerFrame();
}
function stopVisualizer() {
cancelAnimationFrame(vizAnimId);
resizeVizCanvas();
const W = vizCanvas.width;
const H = vizCanvas.height;
vizCtx.clearRect(0, 0, W, H);
// Baseline resting line
vizCtx.strokeStyle = 'rgba(255, 255, 255, 0.1)';
vizCtx.lineWidth = 1;
vizCtx.beginPath();
const restY = vizModes[currentVizModeIdx] === 'mirror pulse' ? (H / 2) : (H * 0.92);
vizCtx.moveTo(0, restY);
vizCtx.lineTo(W, restY);
vizCtx.stroke();
vizIdleMsg.style.opacity = '1';
}
function drawVisualizerFrame() {
vizAnimId = requestAnimationFrame(drawVisualizerFrame);
if (document.hidden) return; // Pause rendering on inactive tab
const W = vizCanvas.width;
const H = vizCanvas.height;
if (W === 0 || H === 0) return;
vizCtx.clearRect(0, 0, W, H);
analyser.getByteFrequencyData(sharedFreqData);
const freqData = sharedFreqData;
const bufLen = sharedFreqData.length;
const mode = vizModes[currentVizModeIdx];
if (mode === 'spectrum bars') {
// Mode 1: Squared Glowing White Spectrum Bars
const barCount = 26;
const gap = W * 0.012;
const barW = (W - gap * (barCount + 1)) / barCount;
const step = Math.floor(bufLen / barCount);
const maxH = H * 0.82;
const baseY = H * 0.92;
for (let i = 0; i < barCount; i++) {
let sum = 0;
for (let k = 0; k < step; k++) sum += freqData[i * step + k];
const ratio = (sum / step) / 255;
const bh = ratio * maxH;
const x = gap + i * (barW + gap);
const y = baseY - bh;
const alpha = 0.25 + ratio * 0.75;
const grad = vizCtx.createLinearGradient(0, y, 0, baseY);
grad.addColorStop(0, `rgba(255, 255, 255, ${alpha})`);
grad.addColorStop(0.5, `rgba(220, 220, 220, ${alpha * 0.6})`);
grad.addColorStop(1, `rgba(180, 180, 180, ${alpha * 0.1})`);
vizCtx.fillStyle = grad;
vizCtx.fillRect(x, y, barW, bh);
// Glowing white peak cap
if (bh > 4) {
vizCtx.fillStyle = `rgba(255, 255, 255, ${Math.min(1, alpha + 0.3)})`;
vizCtx.shadowColor = 'rgba(255, 255, 255, 0.8)';
vizCtx.shadowBlur = 6;
vizCtx.fillRect(x, y - 3, barW, 2);
vizCtx.shadowBlur = 0;
}
}
// Base line
vizCtx.strokeStyle = 'rgba(255, 255, 255, 0.12)';
vizCtx.lineWidth = 1;
vizCtx.beginPath();
vizCtx.moveTo(0, baseY);
vizCtx.lineTo(W, baseY);
vizCtx.stroke();
} else if (mode === 'mirror pulse') {
// Mode 2: Symmetric Center-Out Pulse
const barCount = 18;
const center = W / 2;
const barW = (W / 2) / (barCount + 1);
const maxH = H * 0.75;
const centerY = H / 2;
for (let i = 0; i < barCount; i++) {
const ratio = freqData[i * 4] / 255;
const bh = ratio * maxH;
const alpha = 0.2 + ratio * 0.8;
vizCtx.fillStyle = `rgba(255, 255, 255, ${alpha})`;
vizCtx.fillRect(center + i * barW, centerY - bh / 2, barW - 2, bh);
vizCtx.fillRect(center - (i + 1) * barW, centerY - bh / 2, barW - 2, bh);
}
vizCtx.strokeStyle = 'rgba(255, 255, 255, 0.2)';
vizCtx.lineWidth = 1;
vizCtx.beginPath();
vizCtx.moveTo(0, centerY);
vizCtx.lineTo(W, centerY);
vizCtx.stroke();
}
}
// ── Unique Cybernetic & Audio-Reactive Background Engine ────────────────
const bgCanvas = document.getElementById('bgCanvas');
const bgCtx = bgCanvas.getContext('2d');
const bgVideo = document.getElementById('bgVideo');
const katToast = document.getElementById('katToast');
let toastTimer = null;
function showToast(text) {
if (!katToast) return;
katToast.textContent = text;
katToast.classList.add('show');
clearTimeout(toastTimer);
toastTimer = setTimeout(() => {
katToast.classList.remove('show');
}, 1900);
}
function safeStorageGet(key, def = null) {
try { return localStorage.getItem(key) || def; } catch(e) { return def; }
}
function safeStorageSet(key, val) {
try { localStorage.setItem(key, val); } catch(e) {}
}
const bgModes = [
{ id: 'liquid-void', name: 'Liquid Void Fluid' },
{ id: 'cyber-horizon', name: 'Cyber 3D Horizon' },
{ id: 'matrix-rain', name: 'KatOS Digital Rain' },
{ id: 'video-ambient', name: 'Ambient Video' }
];
let currentBgModeIdx = 0; // Default: 'liquid-void'
const savedBg = safeStorageGet('katos_bg_mode');
if (savedBg && savedBg !== 'video-ambient') {
const idx = bgModes.findIndex(m => m.id === savedBg);
if (idx !== -1) currentBgModeIdx = idx;
} else {
currentBgModeIdx = 0;
safeStorageSet('katos_bg_mode', 'liquid-void');
}
function setBgMode(modeId, silent = false) {
const idx = bgModes.findIndex(m => m.id === modeId);
if (idx !== -1) {
currentBgModeIdx = idx;
applyBgMode(silent);
}
}
function cycleBgMode() {
currentBgModeIdx = (currentBgModeIdx + 1) % bgModes.length;
applyBgMode(false);
}
function applyBgMode(silent = false) {
const mode = bgModes[currentBgModeIdx];
safeStorageSet('katos_bg_mode', mode.id);
if (mode.id === 'video-ambient') {
bgVideo.style.display = 'block';
tryLoadBgVideo();
setTimeout(() => { bgVideo.style.opacity = '0.55'; }, 20);
} else {
bgVideo.style.opacity = '0';
try { bgVideo.pause(); } catch(e) {}
setTimeout(() => {
if (bgModes[currentBgModeIdx].id !== 'video-ambient') bgVideo.style.display = 'none';
}, 300);
}
if (!silent) showToast(`[ BG: ${mode.name} ]`);
}
// Interactive Mouse Tracking (Position & Kinetic Velocity)
const bgMouse = {
x: window.innerWidth / 2,
y: window.innerHeight / 2,
targetX: window.innerWidth / 2,
targetY: window.innerHeight / 2,
vx: 0,
vy: 0,
active: false
};
window.addEventListener('mousemove', e => {
bgMouse.targetX = e.clientX;
bgMouse.targetY = e.clientY;
bgMouse.active = true;
}, { passive: true });
window.addEventListener('mouseleave', () => {
bgMouse.active = false;
}, { passive: true });
// Gravitational Shockwaves on Click
const shockwaves = [];
window.addEventListener('mousedown', e => {
if (e.target.closest('.window-body') || e.target.closest('input') || e.target.closest('.player-btn')) return;
if (shockwaves.length > 5) shockwaves.shift();
shockwaves.push({
x: e.clientX,
y: e.clientY,
radius: 10,
maxRadius: Math.max(window.innerWidth, window.innerHeight) * 0.45,
alpha: 0.65,
power: 1.2
});
});
// Mobile Touch Interaction for Liquid Void Fluid
window.addEventListener('touchstart', e => {
if (e.touches.length > 0) {
const t = e.touches[0];
bgMouse.targetX = t.clientX;
bgMouse.targetY = t.clientY;
bgMouse.active = true;
if (!e.target.closest('.window-body') && !e.target.closest('input') && !e.target.closest('button') && !e.target.closest('.player-btn')) {
if (shockwaves.length > 5) shockwaves.shift();
shockwaves.push({
x: t.clientX,
y: t.clientY,
radius: 10,
maxRadius: Math.max(window.innerWidth, window.innerHeight) * 0.45,
alpha: 0.65,
power: 1.2
});
}
}
}, { passive: true });
window.addEventListener('touchmove', e => {
if (e.touches.length > 0) {
bgMouse.targetX = e.touches[0].clientX;
bgMouse.targetY = e.touches[0].clientY;
bgMouse.active = true;
}
}, { passive: true });
window.addEventListener('touchend', () => {
bgMouse.active = false;
}, { passive: true });
// Pause CPU/GPU canvas rendering when user switches tabs or minimizes window
document.addEventListener('visibilitychange', () => {
if (!document.hidden && isPlaying) {
startVisualizer();
}
});
// ── Liquid Void Kinetic Particle Fluid ──────────────────────────────────
const FLUID_PARTICLE_COUNT = 380;
const fluidParticles = [];
function initFluidParticles() {
fluidParticles.length = 0;
const W = bgCanvas.width;
const H = bgCanvas.height;
for (let i = 0; i < FLUID_PARTICLE_COUNT; i++) {
const x = Math.random() * W;
const y = Math.random() * H;
fluidParticles.push({
x: x,
y: y,
oldX: x,
oldY: y,
vx: (Math.random() - 0.5) * 0.5,
vy: (Math.random() - 0.5) * 0.5,
speed: 0.85 + Math.random() * 1.3,
size: 0.7 + Math.random() * 1.7,
alpha: 0.22 + Math.random() * 0.65,
life: Math.random() * 500,
maxLife: 300 + Math.random() * 600
});
}
}
// Incompressible Curl-Noise Flow Field Generator (Zero Allocation & Fast Math)
let curlVx = 0, curlVy = 0;
function computeCurl(x, y, t) {
const s1 = 0.0016;
const s2 = 0.0034;
const t1 = t * 0.00035;
const t2 = t * 0.0003;
const t3 = t * 0.00045;
const vx = -s1 * Math.sin(x * s1 + t1) * Math.sin(y * s1 + t2) + 0.45 * s2 * Math.cos((x + y) * s2 - t3);
const vy = -(s1 * Math.cos(x * s1 + t1) * Math.sin(y * s1 + t2) + 0.45 * s2 * Math.cos((x + y) * s2 - t3));
const invLen = 1 / (Math.sqrt(vx * vx + vy * vy) || 1);
curlVx = vx * invLen;
curlVy = vy * invLen;
}
// Matrix Rain Setup
let mCols = 0;
let mDrops = [];
const mChars = '01KATOSOIUPOYTARCHLINUX7942#$><;:~^._.^';
function initMatrixDrops() {
mCols = Math.floor(bgCanvas.width / 18);
mDrops = Array.from({ length: mCols }, () => Math.floor(Math.random() * -45));
}
let lastTrackedWidth = window.innerWidth;
function resizeBgCanvas() {
bgCanvas.width = window.innerWidth;
bgCanvas.height = window.innerHeight;
initFluidParticles();
initMatrixDrops();
renderFastfetch();
if (typeof resizeVizCanvas === 'function') resizeVizCanvas();
}
window.addEventListener('resize', () => {
resizeBgCanvas();
if (Math.abs(window.innerWidth - lastTrackedWidth) > 60) {
lastTrackedWidth = window.innerWidth;
autoSpaceWindows();
}
});
resizeBgCanvas();
// Audio frequency extractor (zero garbage collection overhead)
function getAudioBeat() {
if (!isPlaying || !analyser) return { bass: 0, mid: 0, energy: 0 };
analyser.getByteFrequencyData(sharedFreqData);
let bSum = 0; for (let i = 0; i < 5; i++) bSum += sharedFreqData[i];
let mSum = 0; for (let i = 5; i < 20; i++) mSum += sharedFreqData[i];
const bass = (bSum * 0.2) * 0.00392156862745098;
const mid = (mSum * 0.06666666666666667) * 0.00392156862745098;
return { bass, mid, energy: (bass * 0.65 + mid * 0.35) };
}
let lastAudioBeat = 0;
let gridOffset = 0;
function renderBgFrame(timestamp) {
requestAnimationFrame(renderBgFrame);
if (document.hidden) return; // Full GPU/CPU pause on inactive tab
const W = bgCanvas.width;
const H = bgCanvas.height;
if (W === 0 || H === 0) return;
// Mouse velocity & position tracking
bgMouse.vx = (bgMouse.targetX - bgMouse.x);
bgMouse.vy = (bgMouse.targetY - bgMouse.y);
bgMouse.x += bgMouse.vx * 0.1;
bgMouse.y += bgMouse.vy * 0.1;
const audio = getAudioBeat();
// Update shockwaves
for (let i = shockwaves.length - 1; i >= 0; i--) {
const sw = shockwaves[i];
sw.radius += 6.5;
sw.alpha *= 0.955;
if (sw.radius > sw.maxRadius || sw.alpha < 0.01) {
shockwaves.splice(i, 1);
}
}
const currentMode = bgModes[currentBgModeIdx].id;
if (currentMode === 'video-ambient') {
bgCtx.clearRect(0, 0, W, H);
return;
}
if (currentMode === 'matrix-rain') {
// High-definition monochrome matrix rain
bgCtx.fillStyle = 'rgba(0, 0, 0, 0.09)';
bgCtx.fillRect(0, 0, W, H);
bgCtx.font = '12px "JetBrains Mono", monospace';
for (let i = 0; i < mDrops.length; i++) {
const char = mChars.charAt(Math.floor(Math.random() * mChars.length));
const x = i * 18;
const y = mDrops[i] * 18;
if (y >= 0) {
bgCtx.fillStyle = '#ffffff';
bgCtx.shadowColor = '#ffffff';
bgCtx.shadowBlur = 8;
bgCtx.fillText(char, x, y);
bgCtx.shadowBlur = 0;
bgCtx.fillStyle = 'rgba(210, 210, 210, 0.45)';
const prevChar = mChars.charAt(Math.floor(Math.random() * mChars.length));
bgCtx.fillText(prevChar, x, y - 18);
}
if (y > H && Math.random() > 0.978) mDrops[i] = 0;
mDrops[i]++;
}
return;
}
if (currentMode === 'liquid-void') {
// ── "Liquid Void" Incompressible Kinetic Particle Fluid ─────────────
bgCtx.fillStyle = 'rgba(0, 0, 0, 0.14)';
bgCtx.fillRect(0, 0, W, H);
// Render expanding shockwaves
for (let i = 0; i < shockwaves.length; i++) {
const sw = shockwaves[i];
bgCtx.strokeStyle = `rgba(255, 255, 255, ${sw.alpha * 0.35})`;
bgCtx.lineWidth = 1.2;
bgCtx.beginPath();
bgCtx.arc(sw.x, sw.y, sw.radius, 0, Math.PI * 2);
bgCtx.stroke();
}
const flowSpeed = (1.4 + audio.bass * 2.2);
for (let i = 0; i < fluidParticles.length; i++) {
const p = fluidParticles[i];
p.oldX = p.x;
p.oldY = p.y;
// 1. Natural Incompressible Curl Flow Field (Zero Allocation)
computeCurl(p.x, p.y, timestamp);
p.vx += curlVx * 0.22 * p.speed;
p.vy += curlVy * 0.22 * p.speed;
// 2. Interactive Mouse Vortex & Kinetic Attraction
if (bgMouse.active) {
const mdx = bgMouse.x - p.x;
const mdy = bgMouse.y - p.y;
const mDistSq = mdx * mdx + mdy * mdy;
if (mDistSq < 48400 && mDistSq > 0.0001) {
const mDist = Math.sqrt(mDistSq);
const factor = (1 - mDist * 0.00454545);
const invMDist = 1 / mDist;
// Tangential vortex whirlpool force
const swirlX = -mdy * invMDist;
const swirlY = mdx * invMDist;
p.vx += swirlX * factor * 2.8;
p.vy += swirlY * factor * 2.8;
// Gentle radial pull into the vortex center
p.vx += (mdx * invMDist) * factor * 1.1;
p.vy += (mdy * invMDist) * factor * 1.1;
// Kinetic velocity transfer from rapid cursor movement
p.vx += bgMouse.vx * factor * 0.18;
p.vy += bgMouse.vy * factor * 0.18;
}
}
// 3. Shockwave Impulse on particles
for (let k = 0; k < shockwaves.length; k++) {
const sw = shockwaves[k];
const sdx = p.x - sw.x;
const sdy = p.y - sw.y;
const sDist = Math.sqrt(sdx * sdx + sdy * sdy);
const ringDist = Math.abs(sDist - sw.radius);
if (ringDist < 45 && sDist > 0.001) {
const push = (1 - ringDist * 0.02222) * 3.5 * sw.alpha * sw.power;
const invSDist = 1 / sDist;
p.vx += (sdx * invSDist) * push;
p.vy += (sdy * invSDist) * push;
}
}
// Fluid viscosity / drag
p.vx *= 0.92;
p.vy *= 0.92;
p.x += p.vx * flowSpeed;
p.y += p.vy * flowSpeed;
// Boundary wrapping with clean respawn
p.life++;
if (p.x < -10 || p.x > W + 10 || p.y < -10 || p.y > H + 10 || p.life > p.maxLife) {
p.x = Math.random() * W;
p.y = Math.random() * H;
p.oldX = p.x;
p.oldY = p.y;
p.vx = 0;
p.vy = 0;
p.life = 0;
}
// 4. Render Silky Luminous Trail
const vMag = Math.sqrt(p.vx * p.vx + p.vy * p.vy);
const glowAlpha = Math.min(1, p.alpha * (0.8 + audio.bass * 0.9) + vMag * 0.08);
bgCtx.strokeStyle = `rgba(255, 255, 255, ${glowAlpha})`;
bgCtx.lineWidth = p.size * (1 + audio.bass * 0.45);
bgCtx.beginPath();
bgCtx.moveTo(p.oldX, p.oldY);
bgCtx.lineTo(p.x, p.y);
bgCtx.stroke();
// Bright stardust head point
bgCtx.fillStyle = `rgba(255, 255, 255, ${Math.min(1, glowAlpha + 0.3)})`;
bgCtx.fillRect(p.x - p.size / 2, p.y - p.size / 2, p.size, p.size);
}
return;
}
if (currentMode === 'cyber-horizon') {
// ── Clean 3D Perspective Horizon (Zero text or clutter) ──────────────
bgCtx.fillStyle = 'rgba(0, 0, 0, 0.22)';
bgCtx.fillRect(0, 0, W, H);
const horizonY = H * 0.65;
// Horizon glow
const hGrad = bgCtx.createLinearGradient(0, horizonY - 35, 0, horizonY + 25);
hGrad.addColorStop(0, 'rgba(255, 255, 255, 0)');
hGrad.addColorStop(0.5, `rgba(255, 255, 255, ${0.1 + audio.bass * 0.2})`);
hGrad.addColorStop(1, 'rgba(255, 255, 255, 0)');
bgCtx.fillStyle = hGrad;
bgCtx.fillRect(0, horizonY - 35, W, 60);
// Horizon sharp divider
bgCtx.strokeStyle = `rgba(255, 255, 255, ${0.4 + audio.bass * 0.4})`;
bgCtx.lineWidth = 1;
bgCtx.beginPath();
bgCtx.moveTo(0, horizonY);
bgCtx.lineTo(W, horizonY);
bgCtx.stroke();
// Longitudinal radiating lines
const lineCount = 28;
const centerX = W / 2;
const bottomSpread = W * 1.4;
bgCtx.lineWidth = 1;
for (let i = -lineCount / 2; i <= lineCount / 2; i++) {
const topX = centerX + i * 16;
const bottomX = centerX + (i / (lineCount / 2)) * (bottomSpread / 2);
const lineAlpha = 0.04 + (1 - Math.abs(i) / (lineCount / 2)) * 0.12 + audio.bass * 0.08;
bgCtx.strokeStyle = `rgba(255, 255, 255, ${lineAlpha})`;
bgCtx.beginPath();
bgCtx.moveTo(topX, horizonY);
bgCtx.lineTo(bottomX, H);
bgCtx.stroke();
}
// Transverse rolling perspective lines
gridOffset = (gridOffset + 0.005 + audio.bass * 0.01) % 1;
const transLines = 14;
for (let k = 0; k < transLines; k++) {
const progress = (k + gridOffset) / transLines;
const py = horizonY + (H - horizonY) * Math.pow(progress, 2.4);
const tAlpha = Math.min(0.35, progress * 0.38 + audio.bass * 0.15);
bgCtx.strokeStyle = `rgba(255, 255, 255, ${tAlpha})`;
bgCtx.beginPath();
const stepX = 20;
for (let x = 0; x <= W; x += stepX) {
const wave = Math.sin(x * 0.007 + timestamp * 0.003) * audio.bass * 14 * progress;
if (x === 0) bgCtx.moveTo(x, py + wave);
else bgCtx.lineTo(x, py + wave);
}
bgCtx.stroke();
}
}
}
requestAnimationFrame(renderBgFrame);
applyBgMode(true);
// ── Desktop Right-Click Context Menu ─────────────────────────────────────
const ctxMenu = document.getElementById('ctxMenu');
document.addEventListener('contextmenu', e => {
if (e.target.closest('.window-body') || e.target.closest('input')) return;
e.preventDefault();
ctxMenu.style.display = 'block';
let x = e.clientX;
let y = e.clientY;
if (x + 190 > window.innerWidth) x = window.innerWidth - 195;
if (y + 200 > window.innerHeight) y = window.innerHeight - 205;
ctxMenu.style.left = x + 'px';
ctxMenu.style.top = y + 'px';
});
document.addEventListener('click', e => {
if (!e.target.closest('.context-menu')) {
ctxMenu.style.display = 'none';
}
});
// ── Clock ────────────────────────────────────────────────────────────────
function updateClock() {
const now = new Date();
const h = String(now.getHours()).padStart(2, '0');
const m = String(now.getMinutes()).padStart(2, '0');
const s = String(now.getSeconds()).padStart(2, '0');
document.getElementById('clock').textContent = `${h}:${m}:${s}`;
}
updateClock();
setInterval(updateClock, 1000);
// ── Background Video Loader ──────────────────────────────────────────────
async function tryLoadBgVideo() {
if (bgModes[currentBgModeIdx].id !== 'video-ambient') return;
for (const name of ['backgroundVideo.mp4', 'backgroundVideo.webm']) {
try {
const res = await fetch(name);
if (res.ok) {
const url = URL.createObjectURL(await res.blob());
bgVideo.src = url;
bgVideo.load();
bgVideo.play().catch(() => {});
return;
}
} catch (e) {}
}
}
// ── Loading Screen Enter ─────────────────────────────────────────────────
function enterKatOS() {
const loadingScreen = document.getElementById('loadingScreen');
if (loadingScreen && !loadingScreen.classList.contains('hidden')) {
loadingScreen.classList.add('hidden');
applyBgMode(true);
setTimeout(() => {
playMusic();
focusTermInput();
}, 250);
}
}
window.enterKatOS = enterKatOS;
const loadingScreen = document.getElementById('loadingScreen');
if (loadingScreen) {
loadingScreen.addEventListener('click', enterKatOS);
}
// Keyboard Shortcut (Space to play/pause music, Enter/Space to enter from boot screen)
window.addEventListener('keydown', e => {
const loadingEl = document.getElementById('loadingScreen');
if (loadingEl && !loadingEl.classList.contains('hidden')) {
e.preventDefault();
enterKatOS();
return;
}
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
if (e.code === 'Space') {
e.preventDefault();
togglePlay();
}
});
// Initialize System
initWindows();
</script>
</body>
</html>