formatting

This commit is contained in:
Jay
2026-03-21 15:05:08 +00:00
parent 2100334f1f
commit 13c8b0a28e
26 changed files with 3176 additions and 2631 deletions

10
404.css
View File

@@ -17,7 +17,7 @@ body {
} }
.nf-code { .nf-code {
font-family: 'Fraunces', serif; font-family: "Fraunces", serif;
font-size: clamp(7rem, 22vw, 16rem); font-size: clamp(7rem, 22vw, 16rem);
font-weight: 600; font-weight: 600;
line-height: 1; line-height: 1;
@@ -29,7 +29,6 @@ body {
animation: fadeUp 0.6s 0.1s forwards; animation: fadeUp 0.6s 0.1s forwards;
} }
.nf-eyebrow { .nf-eyebrow {
font-size: 0.7rem; font-size: 0.7rem;
letter-spacing: 0.2em; letter-spacing: 0.2em;
@@ -45,7 +44,7 @@ body {
.nf-eyebrow::before, .nf-eyebrow::before,
.nf-eyebrow::after { .nf-eyebrow::after {
content: ''; content: "";
display: block; display: block;
height: 1px; height: 1px;
width: 3rem; width: 3rem;
@@ -54,7 +53,7 @@ body {
} }
.nf-heading { .nf-heading {
font-family: 'Fraunces', serif; font-family: "Fraunces", serif;
font-size: clamp(1.6rem, 4vw, 2.8rem); font-size: clamp(1.6rem, 4vw, 2.8rem);
font-weight: 600; font-weight: 600;
color: var(--text); color: var(--text);
@@ -153,7 +152,8 @@ body {
} }
@keyframes blink { @keyframes blink {
0%, 100% { 0%,
100% {
opacity: 1; opacity: 1;
} }
50% { 50% {

View File

@@ -1,23 +1,22 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head>
<head> <meta charset="UTF-8" />
<meta charset="UTF-8"/> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>404 — Havox</title> <title>404 — Havox</title>
<meta name="description" content="Page not found."/> <meta name="description" content="Page not found." />
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com" />
<link <link
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,400;0,700;1,400&family=Fraunces:ital,opsz,wght@0,9..144,300;0,9..144,600;1,9..144,300&display=swap" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,400;0,700;1,400&family=Fraunces:ital,opsz,wght@0,9..144,300;0,9..144,600;1,9..144,300&display=swap"
rel="stylesheet"> rel="stylesheet"
<link rel="stylesheet" href="style.css"/> />
<link rel="stylesheet" href="404.css"/> <link rel="stylesheet" href="style.css" />
</head> <link rel="stylesheet" href="404.css" />
</head>
<body> <body>
<!-- ─── Nav ─────────────────────────────────────────────────── -->
<!-- ─── Nav ─────────────────────────────────────────────────── --> <nav>
<nav>
<a href="index.html" class="nav-logo">havox</a> <a href="index.html" class="nav-logo">havox</a>
<ul class="nav-links"> <ul class="nav-links">
<li><a href="index.html#about">about</a></li> <li><a href="index.html#about">about</a></li>
@@ -25,18 +24,17 @@
<li><a href="index.html#projects">projects</a></li> <li><a href="index.html#projects">projects</a></li>
<li><a href="index.html#contact">contact</a></li> <li><a href="index.html#contact">contact</a></li>
</ul> </ul>
</nav> </nav>
<!-- ─── 404 ──────────────────────────────────────────────────── -->
<main class="not-found">
<!-- ─── 404 ──────────────────────────────────────────────────── -->
<main class="not-found">
<div class="nf-code">404</div> <div class="nf-code">404</div>
<p class="nf-eyebrow">page not found</p> <p class="nf-eyebrow">page not found</p>
<h1 class="nf-heading">This page <em>doesn't exist</em></h1> <h1 class="nf-heading">This page <em>doesn't exist</em></h1>
<p class="nf-desc"> <p class="nf-desc">
Either this URL was wrong, something got moved, or you've stumbled onto a Either this URL was wrong, something got moved, or you've stumbled onto
dead link I haven't cleaned up yet. Wouldn't be the first time. a dead link I haven't cleaned up yet. Wouldn't be the first time.
</p> </p>
<div class="nf-terminal" aria-hidden="true"> <div class="nf-terminal" aria-hidden="true">
@@ -49,10 +47,18 @@
<span class="nf-prompt">~</span> <span class="nf-prompt">~</span>
<span class="nf-cmd">curl -I <span id="typed-path"></span></span> <span class="nf-cmd">curl -I <span id="typed-path"></span></span>
</div> </div>
<div class="nf-terminal-line" id="nf-response" style="opacity:0; transition: opacity 0.3s;"> <div
class="nf-terminal-line"
id="nf-response"
style="opacity: 0; transition: opacity 0.3s"
>
<span class="nf-err">HTTP/1.1 404 Not Found</span> <span class="nf-err">HTTP/1.1 404 Not Found</span>
</div> </div>
<div class="nf-terminal-line" id="nf-hint" style="opacity:0; transition: opacity 0.3s;"> <div
class="nf-terminal-line"
id="nf-hint"
style="opacity: 0; transition: opacity 0.3s"
>
<span class="nf-comment"># maybe try going home?</span> <span class="nf-comment"># maybe try going home?</span>
</div> </div>
<div class="nf-terminal-line"> <div class="nf-terminal-line">
@@ -64,20 +70,17 @@
<div class="nf-actions"> <div class="nf-actions">
<a href="/" class="btn btn-primary">← Back home</a> <a href="/" class="btn btn-primary">← Back home</a>
</div> </div>
</main>
</main> <!-- ─── Footer ─────────────────────────────────────────────────── -->
<footer>
<!-- ─── Footer ─────────────────────────────────────────────────── -->
<footer>
<span>umbra.mom — John Gatward</span> <span>umbra.mom — John Gatward</span>
<span> <span>
Source code (selfhosted): <a href="https://gitea.umbra.mom/jay/havox">repository</a> Source code (selfhosted):
<a href="https://gitea.umbra.mom/jay/havox">repository</a>
</span> </span>
</footer> </footer>
<script src="404.js"> <script src="404.js"></script>
</script> </body>
</body>
</html> </html>

View File

@@ -1,23 +1,21 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head>
<head> <meta charset="UTF-8" />
<meta charset="UTF-8"/> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Havox</title> <title>Havox</title>
<meta name="description" <meta name="description" content="John Gatward's portfolio" />
content="John Gatward's portfolio"/> <link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link <link
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,400;0,700;1,400&family=Fraunces:ital,opsz,wght@0,9..144,300;0,9..144,600;1,9..144,300&display=swap" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,400;0,700;1,400&family=Fraunces:ital,opsz,wght@0,9..144,300;0,9..144,600;1,9..144,300&display=swap"
rel="stylesheet"> rel="stylesheet"
<link rel="stylesheet" href="style.css"/> />
</head> <link rel="stylesheet" href="style.css" />
</head>
<body> <body>
<!-- ─── Nav ─────────────────────────────────────────────────── -->
<!-- ─── Nav ─────────────────────────────────────────────────── --> <nav>
<nav>
<a href="#hero" class="nav-logo">havox</a> <a href="#hero" class="nav-logo">havox</a>
<ul class="nav-links"> <ul class="nav-links">
<li><a href="#about">about</a></li> <li><a href="#about">about</a></li>
@@ -25,130 +23,169 @@
<li><a href="#projects">projects</a></li> <li><a href="#projects">projects</a></li>
<li><a href="#contact">contact</a></li> <li><a href="#contact">contact</a></li>
</ul> </ul>
</nav> </nav>
<!-- ─── Hero ─────────────────────────────────────────────────── --> <!-- ─── Hero ─────────────────────────────────────────────────── -->
<section id="hero"> <section id="hero">
<p class="hero-eyebrow">// havox.org - v4</p> <p class="hero-eyebrow">// havox.org - v4</p>
<h1 class="hero-name">John <em>Gatward</em></h1> <h1 class="hero-name">John <em>Gatward</em></h1>
<p class="hero-subtitle">Software Engineer</p> <p class="hero-subtitle">Software Engineer</p>
<p class="hero-desc"> <p class="hero-desc">
<span class="hero-by-line"><span class="hero-by">Backend engineer</span><span class="hero-by-role">by <span class="hero-by-line"
trade</span></span> ><span class="hero-by">Backend engineer</span
<span class="hero-by-line"><span class="hero-by">Developer enthusiast</span><span class="hero-by-role">by ><span class="hero-by-role">by trade</span></span
curiosity</span></span> >
<span class="hero-by-line"><span class="hero-by">Unofficial family cloud engineer</span><span <span class="hero-by-line"
class="hero-by-role">by necessity</span></span> ><span class="hero-by">Developer enthusiast</span
><span class="hero-by-role">by curiosity</span></span
>
<span class="hero-by-line"
><span class="hero-by">Unofficial family cloud engineer</span
><span class="hero-by-role">by necessity</span></span
>
</p> </p>
<div class="hero-links"> <div class="hero-links">
<a href="#projects" class="btn btn-primary">View projects</a> <a href="#projects" class="btn btn-primary">View projects</a>
<a href="#contact" class="btn btn-ghost">Contact me</a> <a href="#contact" class="btn btn-ghost">Contact me</a>
</div> </div>
</section> </section>
<div class="divider"></div> <div class="divider"></div>
<!-- ─── About ─────────────────────────────────────────────────── --> <!-- ─── About ─────────────────────────────────────────────────── -->
<section id="about" class="about-section"> <section id="about" class="about-section">
<div class="reveal"> <div class="reveal">
<p class="section-label">about</p> <p class="section-label">about</p>
<h2 class="section-heading">Havox & <em>me</em></h2> <h2 class="section-heading">Havox & <em>me</em></h2>
</div> </div>
<div class="about-columns"> <div class="about-columns">
<!-- Bio blocks --> <!-- Bio blocks -->
<div class="about-grid reveal" style="transition-delay:0.05s"> <div class="about-grid reveal" style="transition-delay: 0.05s">
<div class="about-block"> <div class="about-block">
<p>Havox started in 2017 as a place to dump whatever projects I had written. Four redesigns and several <p>
dead domains later, it's still going. Less a portfolio, more a scrapbook.</p> Havox started in 2017 as a place to dump whatever projects I had
written. Four redesigns and several dead domains later, it's still
going. Less a portfolio, more a scrapbook.
</p>
</div> </div>
<div class="about-block"> <div class="about-block">
<span class="about-block-tag tag tag-blue">day job</span> <span class="about-block-tag tag tag-blue">day job</span>
<p>Backend engineer at <strong>Sainsbury's</strong> Supply Chain &amp; Logistics. It's good fun.</p> <p>
Backend engineer at <strong>Sainsbury's</strong> Supply Chain
&amp; Logistics. It's good fun.
</p>
</div> </div>
<div class="about-block"> <div class="about-block">
<span class="about-block-tag tag tag-teal">dev</span> <span class="about-block-tag tag tag-teal">dev</span>
<p>Fascinated with new languages, frameworks, and shiny tech I probably dont need. Constantly <p>
learning just enough to build something slightly cooler next time.</p> Fascinated with new languages, frameworks, and shiny tech I
probably dont need. Constantly learning just enough to build
something slightly cooler next time.
</p>
</div> </div>
<div class="about-block"> <div class="about-block">
<span class="about-block-tag tag tag-yellow">linux</span> <span class="about-block-tag tag tag-yellow">linux</span>
<p>Long-time Linux user and distro-hopper. Self-hosted server running 24/7 for friends and family who <p>
didn't ask for it but <s>probably</s> definitely appreciate it.</p> Long-time Linux user and distro-hopper. Self-hosted server running
24/7 for friends and family who didn't ask for it but
<s>probably</s> definitely appreciate it.
</p>
</div> </div>
</div> </div>
<!-- Version timeline --> <!-- Version timeline -->
<div class="reveal" style="transition-delay:0.15s"> <div class="reveal" style="transition-delay: 0.15s">
<p class="section-label">version history</p> <p class="section-label">version history</p>
<div class="version-timeline"> <div class="version-timeline">
<div class="version-item"> <div class="version-item">
<div class="v-dot v-dot--current"></div> <div class="v-dot v-dot--current"></div>
<div class="v-content"> <div class="v-content">
<div class="v-title">Havox V4 <span class="v-current-badge">current</span></div> <div class="v-title">
Havox V4 <span class="v-current-badge">current</span>
</div>
<div class="v-year">2026 → present</div> <div class="v-year">2026 → present</div>
<div class="v-desc">Catppuccin Mocha. Single page. Professional.</div> <div class="v-desc">
Catppuccin Mocha. Single page. Professional.
</div>
</div> </div>
</div> </div>
<a class="version-item version-item--link" href="archive/v3/index.html"> <a
class="version-item version-item--link"
href="archive/v3/index.html"
>
<div class="v-dot"></div> <div class="v-dot"></div>
<div class="v-content"> <div class="v-content">
<div class="v-title">Havox V3 <span class="v-link-arrow"></span></div> <div class="v-title">
Havox V3 <span class="v-link-arrow"></span>
</div>
<div class="v-year">2022 → 2025</div> <div class="v-year">2022 → 2025</div>
<div class="v-desc">Borrowed a professional template and tried to make it suit my needs. Wasn't <div class="v-desc">
very 'me'. Borrowed a professional template and tried to make it suit my
needs. Wasn't very 'me'.
</div> </div>
</div> </div>
</a> </a>
<a class="version-item version-item--link" href="archive/v2/index.html"> <a
class="version-item version-item--link"
href="archive/v2/index.html"
>
<div class="v-dot"></div> <div class="v-dot"></div>
<div class="v-content"> <div class="v-content">
<div class="v-title">Umbra.cyou <span class="v-link-arrow"></span></div> <div class="v-title">
Umbra.cyou <span class="v-link-arrow"></span>
</div>
<div class="v-year">2019 → 2022</div> <div class="v-year">2019 → 2022</div>
<div class="v-desc">Built from the ground up focusing on a minimal clean design. Stole my code <div class="v-desc">
VS Code theme for a colour palette. Built from the ground up focusing on a minimal clean design.
Stole my code VS Code theme for a colour palette.
</div> </div>
</div> </div>
</a> </a>
<a class="version-item version-item--link" href="archive/v1/index.html"> <a
class="version-item version-item--link"
href="archive/v1/index.html"
>
<div class="v-dot"></div> <div class="v-dot"></div>
<div class="v-content"> <div class="v-content">
<div class="v-title">Havox.org <span class="v-link-arrow"></span></div> <div class="v-title">
Havox.org <span class="v-link-arrow"></span>
</div>
<div class="v-year">2016 → 2019</div> <div class="v-year">2016 → 2019</div>
<div class="v-desc">Wrote the entire site in one php file. Hosted on a £1/year VPS. <div class="v-desc">
Wrote the entire site in one php file. Hosted on a £1/year
VPS.
</div> </div>
</div> </div>
</a> </a>
</div> </div>
</div> </div>
</div> </div>
</section>
</section> <div class="divider"></div>
<div class="divider"></div> <!-- ─── Skills ─────────────────────────────────────────────────── -->
<section id="skills">
<!-- ─── Skills ─────────────────────────────────────────────────── -->
<section id="skills">
<p class="section-label reveal">skills</p> <p class="section-label reveal">skills</p>
<h2 class="section-heading reveal" style="transition-delay:0.05s">What I <em>know</em></h2> <h2 class="section-heading reveal" style="transition-delay: 0.05s">
What I <em>know</em>
</h2>
<div class="skills-grid"> <div class="skills-grid">
<div class="skill-card reveal"> <div class="skill-card reveal">
<span class="skill-card-icon"></span> <span class="skill-card-icon"></span>
<h3>Backend</h3> <h3>Backend</h3>
<p>Professional experience designing, building and deploying services. Using a modern tech stack in the <p>
Supply Chain & Logistics industry.</p> Professional experience designing, building and deploying services.
Using a modern tech stack in the Supply Chain & Logistics industry.
</p>
<div class="tag-row"> <div class="tag-row">
<span class="tag tag-blue">Java</span> <span class="tag tag-blue">Java</span>
<span class="tag tag-green">Spring Boot</span> <span class="tag tag-green">Spring Boot</span>
@@ -157,22 +194,26 @@
<span class="tag tag-sky">MongoDB</span> <span class="tag tag-sky">MongoDB</span>
</div> </div>
</div> </div>
<div class="skill-card reveal" style="transition-delay:0.08s"> <div class="skill-card reveal" style="transition-delay: 0.08s">
<span class="skill-card-icon">🎨</span> <span class="skill-card-icon">🎨</span>
<h3>Frontend & Creative</h3> <h3>Frontend & Creative</h3>
<p>Comfortable with React and CSS. I've always enjoyed visualising algorithms interactively - it's way more <p>
fun than a console output.</p> Comfortable with React and CSS. I've always enjoyed visualising
algorithms interactively - it's way more fun than a console output.
</p>
<div class="tag-row"> <div class="tag-row">
<span class="tag tag-teal">React</span> <span class="tag tag-teal">React</span>
<span class="tag tag-green">TypeScript</span> <span class="tag tag-green">TypeScript</span>
<span class="tag tag-blue">HTML/CSS</span> <span class="tag tag-blue">HTML/CSS</span>
</div> </div>
</div> </div>
<div class="skill-card reveal" style="transition-delay:0.16s"> <div class="skill-card reveal" style="transition-delay: 0.16s">
<span class="skill-card-icon">🖥</span> <span class="skill-card-icon">🖥</span>
<h3>Systems & Infra</h3> <h3>Systems & Infra</h3>
<p>Long-term Linux & Vim user. Self-hosted web & media servers. Using industry practices when it comes to <p>
networks & security.</p> Long-term Linux & Vim user. Self-hosted web & media servers. Using
industry practices when it comes to networks & security.
</p>
<div class="tag-row"> <div class="tag-row">
<span class="tag tag-green">Linux</span> <span class="tag tag-green">Linux</span>
<span class="tag tag-teal">Docker</span> <span class="tag tag-teal">Docker</span>
@@ -180,12 +221,17 @@
<span class="tag tag-peach">Wireguard</span> <span class="tag tag-peach">Wireguard</span>
</div> </div>
</div> </div>
<div class="skill-card skill-card--wide reveal" style="transition-delay:0.24s"> <div
class="skill-card skill-card--wide reveal"
style="transition-delay: 0.24s"
>
<span class="skill-card-icon">🎓</span> <span class="skill-card-icon">🎓</span>
<h3>Academic Breadth</h3> <h3>Academic Breadth</h3>
<p>C, C++, x86_64 assembly, Haskell, R &amp; MatLab from uni. Modules in Advanced networking, <p>
algorithms, compilers, C, C++, x86_64 assembly, Haskell, R &amp; MatLab from uni. Modules
graphics, cryptography, malware analysis & many more.</p> in Advanced networking, algorithms, compilers, graphics,
cryptography, malware analysis & many more.
</p>
<div class="tag-row"> <div class="tag-row">
<span class="tag tag-blue">C / C++</span> <span class="tag tag-blue">C / C++</span>
<span class="tag tag-mauve">Haskell</span> <span class="tag tag-mauve">Haskell</span>
@@ -195,28 +241,38 @@
</div> </div>
</div> </div>
</div> </div>
</section> </section>
<div class="divider"></div> <div class="divider"></div>
<!-- ─── Projects ──────────────────────────────────────────────── --> <!-- ─── Projects ──────────────────────────────────────────────── -->
<section id="projects"> <section id="projects">
<p class="section-label reveal">projects</p> <p class="section-label reveal">projects</p>
<h2 class="section-heading reveal" style="transition-delay:0.05s">Things I <em>built</em></h2> <h2 class="section-heading reveal" style="transition-delay: 0.05s">
<p class="section-intro reveal" style="transition-delay:0.1s"> Things I <em>built</em>
A timeline of my projects and an overview of my journey as a developer. Often times getting inspired and trying </h2>
to recreate something cool I saw. <p class="section-intro reveal" style="transition-delay: 0.1s">
A timeline of my projects and an overview of my journey as a developer.
Often times getting inspired and trying to recreate something cool I
saw.
</p> </p>
<div class="projects-subheading reveal" style="transition-delay:0.12s"> <div class="projects-subheading reveal" style="transition-delay: 0.12s">
<span>Featured projects</span> <span>Featured projects</span>
</div> </div>
<div class="card-grid card-grid--featured reveal" style="transition-delay:0.14s"> <div
class="card-grid card-grid--featured reveal"
style="transition-delay: 0.14s"
>
<a class="card" href="https://wordlesolver.umbra.mom"> <a class="card" href="https://wordlesolver.umbra.mom">
<span class="card-date">9 Feb 2025</span> <span class="card-date">9 Feb 2025</span>
<span class="card-title">Wordle Solver</span> <span class="card-title">Wordle Solver</span>
<span class="card-desc">Built after one too many missed 3-guess games. Now it plays marginally better than me. Uses information theory to recommend the next best guess.</span> <span class="card-desc"
>Built after one too many missed 3-guess games. Now it plays
marginally better than me. Uses information theory to recommend the
next best guess.</span
>
<span class="tag-row"> <span class="tag-row">
<span class="tag tag-yellow">Rust</span> <span class="tag tag-yellow">Rust</span>
<span class="tag tag-teal">React</span> <span class="tag tag-teal">React</span>
@@ -227,7 +283,10 @@
<a class="card" href="https://crackthequote.umbra.mom"> <a class="card" href="https://crackthequote.umbra.mom">
<span class="card-date">13 Apr 2024</span> <span class="card-date">13 Apr 2024</span>
<span class="card-title">Crack the Quote</span> <span class="card-title">Crack the Quote</span>
<span class="card-desc">A substitution-cipher puzzle game with a daily challenge. Built for me and my family to play after getting bored with NYT games.</span> <span class="card-desc"
>A substitution-cipher puzzle game with a daily challenge. Built for
me and my family to play after getting bored with NYT games.</span
>
<span class="tag-row"> <span class="tag-row">
<span class="tag tag-yellow">Rust</span> <span class="tag tag-yellow">Rust</span>
<span class="tag tag-teal">React</span> <span class="tag tag-teal">React</span>
@@ -237,37 +296,73 @@
</a> </a>
</div> </div>
<div class="projects-subheading reveal" style="transition-delay:0.16s"> <div class="projects-subheading reveal" style="transition-delay: 0.16s">
<span>All projects</span> <span>All projects</span>
</div> </div>
<fieldset class="project-filters reveal" style="transition-delay:0.18s" aria-label="Filter projects by tech stack"> <fieldset
<button type="button" class="filter-chip active" data-filter="all">All</button> class="project-filters reveal"
<button type="button" class="filter-chip" data-filter="javascript">JavaScript</button> style="transition-delay: 0.18s"
<button type="button" class="filter-chip" data-filter="python">Python</button> aria-label="Filter projects by tech stack"
<button type="button" class="filter-chip" data-filter="rust">Rust</button> >
<button type="button" class="filter-chip" data-filter="react">React</button> <button type="button" class="filter-chip active" data-filter="all">
<button type="button" class="filter-chip" data-filter="webassembly">WebAssembly</button> All
<button type="button" class="filter-chip" data-filter="p5">p5.js</button> </button>
<button type="button" class="filter-chip" data-filter="javascript">
JavaScript
</button>
<button type="button" class="filter-chip" data-filter="python">
Python
</button>
<button type="button" class="filter-chip" data-filter="rust">
Rust
</button>
<button type="button" class="filter-chip" data-filter="react">
React
</button>
<button type="button" class="filter-chip" data-filter="webassembly">
WebAssembly
</button>
<button type="button" class="filter-chip" data-filter="p5">
p5.js
</button>
<button type="button" class="filter-chip" data-filter="zig">Zig</button> <button type="button" class="filter-chip" data-filter="zig">Zig</button>
<button type="button" class="filter-chip" data-filter="cpp">C / C++</button> <button type="button" class="filter-chip" data-filter="cpp">
C / C++
</button>
</fieldset> </fieldset>
<div id="project-grid" class="card-grid reveal" style="transition-delay:0.2s"> <div
<article class="card project-card card-link" data-tech="python" data-href="https://pubquiz.umbra.mom"> id="project-grid"
class="card-grid reveal"
style="transition-delay: 0.2s"
>
<article
class="card project-card card-link"
data-tech="python"
data-href="https://pubquiz.umbra.mom"
>
<span class="card-date">03 Apr 2025</span> <span class="card-date">03 Apr 2025</span>
<span class="card-title">Pub Quiz Dashboard</span> <span class="card-title">Pub Quiz Dashboard</span>
<span class="card-desc">A Python dashboard to track our local pub quiz performances.</span> <span class="card-desc"
>A Python dashboard to track our local pub quiz performances.</span
>
<span class="tag-row"> <span class="tag-row">
<span class="tag tag-red">Python</span> <span class="tag tag-red">Python</span>
</span> </span>
<span class="card-arrow"></span> <span class="card-arrow"></span>
</article> </article>
<article class="card project-card card-link" data-tech="cpp webassembly" <article
data-href="projects/thin_ice/thin_ice.html"> class="card project-card card-link"
data-tech="cpp webassembly"
data-href="projects/thin_ice/thin_ice.html"
>
<span class="card-date">14 Nov 2024</span> <span class="card-date">14 Nov 2024</span>
<span class="card-title">Thin Ice</span> <span class="card-title">Thin Ice</span>
<span class="card-desc">A small browser tile game inspired by one I played growing up.</span> <span class="card-desc"
>A small browser tile game inspired by one I played growing
up.</span
>
<span class="tag-row"> <span class="tag-row">
<span class="tag tag-blue">C++</span> <span class="tag tag-blue">C++</span>
<span class="tag tag-teal">WebAssembly</span> <span class="tag tag-teal">WebAssembly</span>
@@ -275,10 +370,18 @@
</span> </span>
<span class="card-arrow"></span> <span class="card-arrow"></span>
</article> </article>
<article class="card project-card card-link" data-tech="zig webassembly" data-href="projects/tsp/index.html"> <article
class="card project-card card-link"
data-tech="zig webassembly"
data-href="projects/tsp/index.html"
>
<span class="card-date">20 Mar 2024</span> <span class="card-date">20 Mar 2024</span>
<span class="card-title">Travelling Salesman Problem</span> <span class="card-title">Travelling Salesman Problem</span>
<span class="card-desc">A nearest-neighbor + 2-opt visualiser. Wanted to revisit one of my first projects but with a super optimised tech-stack & algorithm.</span> <span class="card-desc"
>A nearest-neighbor + 2-opt visualiser. Wanted to revisit one of my
first projects but with a super optimised tech-stack &
algorithm.</span
>
<span class="tag-row"> <span class="tag-row">
<span class="tag tag-yellow">Zig</span> <span class="tag tag-yellow">Zig</span>
<span class="tag tag-sky">WebGL</span> <span class="tag tag-sky">WebGL</span>
@@ -286,43 +389,74 @@
</span> </span>
<span class="card-arrow"></span> <span class="card-arrow"></span>
</article> </article>
<article class="card project-card card-link" data-tech="rust" data-href="projects/flocking/index.html"> <article
class="card project-card card-link"
data-tech="rust"
data-href="projects/flocking/index.html"
>
<span class="card-date">31 Jan 2024</span> <span class="card-date">31 Jan 2024</span>
<span class="card-title">Flocking</span> <span class="card-title">Flocking</span>
<span class="card-desc">Rewrote the entire project in Rust from GoLang to compile to wasm. Was fun being able to use DOM controls to affect the simulation.</span> <span class="card-desc"
>Rewrote the entire project in Rust from GoLang to compile to wasm.
Was fun being able to use DOM controls to affect the
simulation.</span
>
<span class="tag-row"> <span class="tag-row">
<span class="tag tag-yellow">Rust</span> <span class="tag tag-yellow">Rust</span>
<span class="tag tag-peach">Raylib</span> <span class="tag tag-peach">Raylib</span>
</span> </span>
<span class="card-arrow"></span> <span class="card-arrow"></span>
</article> </article>
<article class="card project-card card-link" data-tech="javascript" data-href="https://samstoreymusic.com"> <article
class="card project-card card-link"
data-tech="javascript"
data-href="https://samstoreymusic.com"
>
<span class="card-date">1 Nov 2023</span> <span class="card-date">1 Nov 2023</span>
<span class="card-title">samstoreymusic.com</span> <span class="card-title">samstoreymusic.com</span>
<span class="card-desc">A website design and build for a friend working in music. Saving him the hosting fees :)</span> <span class="card-desc"
>A website design and build for a friend working in music. Saving
him the hosting fees :)</span
>
<span class="tag-row"> <span class="tag-row">
<span class="tag tag-mauve">HTML/CSS</span> <span class="tag tag-mauve">HTML/CSS</span>
<span class="tag tag-green">JavaScript</span> <span class="tag tag-green">JavaScript</span>
</span> </span>
<span class="card-arrow"></span> <span class="card-arrow"></span>
</article> </article>
<article class="card project-card card-link" data-tech="javascript react" <article
data-href="projects/cellular_automata/index.html"> class="card project-card card-link"
data-tech="javascript react"
data-href="projects/cellular_automata/index.html"
>
<span class="card-date">21 Jul 2023</span> <span class="card-date">21 Jul 2023</span>
<span class="card-title">Game of Life</span> <span class="card-title">Game of Life</span>
<span class="card-desc">After building Conway's Game of Life in p5.js as one of my first projects, I expanded it into a generic automata simulator.</span> <span class="card-desc"
>After building Conway's Game of Life in p5.js as one of my first
projects, I expanded it into a generic automata simulator.</span
>
<span class="tag-row"> <span class="tag-row">
<span class="tag tag-teal">React</span> <span class="tag tag-teal">React</span>
<span class="tag tag-green">JavaScript</span> <span class="tag tag-green">JavaScript</span>
</span> </span>
<span class="card-arrow"></span> <span class="card-arrow"></span>
</article> </article>
<article class="card project-card card-link" data-tech="cpp webassembly" <article
data-href="projects/percolation/index.html"> class="card project-card card-link"
data-tech="cpp webassembly"
data-href="projects/percolation/index.html"
>
<span class="card-date">5 Feb 2022</span> <span class="card-date">5 Feb 2022</span>
<span class="card-title">Percolation</span> <span class="card-title">Percolation</span>
<span class="card-desc">Watched a brilliant video by <a target="_blank" <span class="card-desc"
href="https://youtu.be/a-767WnbaCQ?si=f3NM95INeoZqzIi8">Spectral Collective</a> and just had to recreate it.</span> >Watched a brilliant video by
<a
target="_blank"
href="https://youtu.be/a-767WnbaCQ?si=f3NM95INeoZqzIi8"
>Spectral Collective</a
>
and just had to recreate it.</span
>
<span class="tag-row"> <span class="tag-row">
<span class="tag tag-blue">C</span> <span class="tag tag-blue">C</span>
<span class="tag tag-peach">RayLib</span> <span class="tag tag-peach">RayLib</span>
@@ -330,89 +464,148 @@
</span> </span>
<span class="card-arrow"></span> <span class="card-arrow"></span>
</article> </article>
<article class="card project-card card-link" data-tech="javascript p5" <article
data-href="projects/cubic_bezier_curve/index.html"> class="card project-card card-link"
data-tech="javascript p5"
data-href="projects/cubic_bezier_curve/index.html"
>
<span class="card-date">1 Oct 2021</span> <span class="card-date">1 Oct 2021</span>
<span class="card-title">Drawing Bézier curves</span> <span class="card-title">Drawing Bézier curves</span>
<span class="card-desc">An interactive animation for how cubic Bézier curves are constructed. Inspirated by this beautiful video by <a <span class="card-desc"
target="_blank" href="https://youtu.be/aVwxzDHniEw?si=mCylw_p-fMSdhyvN">Fraya Holmér</a></span> >An interactive animation for how cubic Bézier curves are
constructed. Inspirated by this beautiful video by
<a
target="_blank"
href="https://youtu.be/aVwxzDHniEw?si=mCylw_p-fMSdhyvN"
>Fraya Holmér</a
></span
>
<span class="tag-row"> <span class="tag-row">
<span class="tag tag-green">JavaScript</span> <span class="tag tag-green">JavaScript</span>
<span class="tag tag-red">p5.js</span> <span class="tag tag-red">p5.js</span>
</span> </span>
<span class="card-arrow"></span> <span class="card-arrow"></span>
</article> </article>
<article class="card project-card card-link" data-tech="javascript p5" <article
data-href="projects/marching_squares.html"> class="card project-card card-link"
data-tech="javascript p5"
data-href="projects/marching_squares.html"
>
<span class="card-date">17 Sep 2021</span> <span class="card-date">17 Sep 2021</span>
<span class="card-title">2D Marching Squares</span> <span class="card-title">2D Marching Squares</span>
<span class="card-desc">I saw the 3D version of this algorithm in a game dev log and was inspired to create this 2d land/sea map generator thing.</span> <span class="card-desc"
>I saw the 3D version of this algorithm in a game dev log and was
inspired to create this 2d land/sea map generator thing.</span
>
<span class="tag-row"> <span class="tag-row">
<span class="tag tag-green">JavaScript</span> <span class="tag tag-green">JavaScript</span>
<span class="tag tag-red">p5.js</span> <span class="tag tag-red">p5.js</span>
</span> </span>
<span class="card-arrow"></span> <span class="card-arrow"></span>
</article> </article>
<article class="card project-card card-link" data-tech="javascript p5" data-href="projects/warping_lines.html"> <article
class="card project-card card-link"
data-tech="javascript p5"
data-href="projects/warping_lines.html"
>
<span class="card-date">2 Apr 2020</span> <span class="card-date">2 Apr 2020</span>
<span class="card-title">Müller-Lyer illusion</span> <span class="card-title">Müller-Lyer illusion</span>
<span class="card-desc">JavaScript visualisation of the Müller-Lyer optical illusion.</span> <span class="card-desc"
>JavaScript visualisation of the Müller-Lyer optical illusion.</span
>
<span class="tag-row"> <span class="tag-row">
<span class="tag tag-green">JavaScript</span> <span class="tag tag-green">JavaScript</span>
<span class="tag tag-red">p5.js</span> <span class="tag tag-red">p5.js</span>
</span> </span>
<span class="card-arrow"></span> <span class="card-arrow"></span>
</article> </article>
<article class="card project-card card-link" data-tech="javascript p5" data-href="projects/fourier_series.html"> <article
class="card project-card card-link"
data-tech="javascript p5"
data-href="projects/fourier_series.html"
>
<span class="card-date">27 Feb 2019</span> <span class="card-date">27 Feb 2019</span>
<span class="card-title">Fourier series</span> <span class="card-title">Fourier series</span>
<span class="card-desc">Builds a square wave from sine components to show Fourier series in motion.</span> <span class="card-desc"
>Builds a square wave from sine components to show Fourier series in
motion.</span
>
<span class="tag-row"> <span class="tag-row">
<span class="tag tag-green">JavaScript</span> <span class="tag tag-green">JavaScript</span>
<span class="tag tag-red">p5.js</span> <span class="tag tag-red">p5.js</span>
</span> </span>
<span class="card-arrow"></span> <span class="card-arrow"></span>
</article> </article>
<article class="card project-card card-link" data-tech="javascript p5" <article
data-href="projects/ellipse_construction.html"> class="card project-card card-link"
data-tech="javascript p5"
data-href="projects/ellipse_construction.html"
>
<span class="card-date">23 Feb 2019</span> <span class="card-date">23 Feb 2019</span>
<span class="card-title">Constructing an ellipse</span> <span class="card-title">Constructing an ellipse</span>
<span class="card-desc">A geometric construction demo inspired by <a target="_blank" <span class="card-desc"
href="https://youtu.be/xdIjYBtnvZU?si=uI66LS_O_eAWqG4e">3Blue1Brown's video</a> on Feynman's lost lecture.</span> >A geometric construction demo inspired by
<a
target="_blank"
href="https://youtu.be/xdIjYBtnvZU?si=uI66LS_O_eAWqG4e"
>3Blue1Brown's video</a
>
on Feynman's lost lecture.</span
>
<span class="tag-row"> <span class="tag-row">
<span class="tag tag-green">JavaScript</span> <span class="tag tag-green">JavaScript</span>
<span class="tag tag-red">p5.js</span> <span class="tag tag-red">p5.js</span>
</span> </span>
<span class="card-arrow"></span> <span class="card-arrow"></span>
</article> </article>
<article class="card project-card card-link" data-tech="javascript p5" <article
data-href="projects/pi_approximation.html"> class="card project-card card-link"
data-tech="javascript p5"
data-href="projects/pi_approximation.html"
>
<span class="card-date">18 Mar 2018</span> <span class="card-date">18 Mar 2018</span>
<span class="card-title">Calculating PI</span> <span class="card-title">Calculating PI</span>
<span class="card-desc">A Monte Carlo approximation of pi using random sampling.</span> <span class="card-desc"
>A Monte Carlo approximation of pi using random sampling.</span
>
<span class="tag-row"> <span class="tag-row">
<span class="tag tag-green">JavaScript</span> <span class="tag tag-green">JavaScript</span>
<span class="tag tag-red">p5.js</span> <span class="tag tag-red">p5.js</span>
</span> </span>
<span class="card-arrow"></span> <span class="card-arrow"></span>
</article> </article>
<article class="card project-card card-link" data-tech="javascript p5" <article
data-href="projects/oscillations_in_3d.html"> class="card project-card card-link"
data-tech="javascript p5"
data-href="projects/oscillations_in_3d.html"
>
<span class="card-date">17 Dec 2017</span> <span class="card-date">17 Dec 2017</span>
<span class="card-title">Oscillations in 3D</span> <span class="card-title">Oscillations in 3D</span>
<span class="card-desc">A JavaScript recreation of a Bees and Bombs animation.</span> <span class="card-desc"
>A JavaScript recreation of a Bees and Bombs animation.</span
>
<span class="tag-row"> <span class="tag-row">
<span class="tag tag-green">JavaScript</span> <span class="tag tag-green">JavaScript</span>
<span class="tag tag-red">p5.js</span> <span class="tag tag-red">p5.js</span>
</span> </span>
<span class="card-arrow"></span> <span class="card-arrow"></span>
</article> </article>
<article class="card project-card card-link" data-tech="javascript p5" <article
data-href="projects/maze_generation/index.html"> class="card project-card card-link"
data-tech="javascript p5"
data-href="projects/maze_generation/index.html"
>
<span class="card-date">13 Nov 2017</span> <span class="card-date">13 Nov 2017</span>
<span class="card-title">Maze generator</span> <span class="card-title">Maze generator</span>
<span class="card-desc">Discovered the <a target="_blank" <span class="card-desc"
href="https://youtu.be/HyK_Q5rrcr4?si=G6g8xDHK0cc_MwhG">Coding Train's channel</a> and learnt coding through following along.</span> >Discovered the
<a
target="_blank"
href="https://youtu.be/HyK_Q5rrcr4?si=G6g8xDHK0cc_MwhG"
>Coding Train's channel</a
>
and learnt coding through following along.</span
>
<span class="tag-row"> <span class="tag-row">
<span class="tag tag-green">JavaScript</span> <span class="tag tag-green">JavaScript</span>
<span class="tag tag-red">p5.js</span> <span class="tag tag-red">p5.js</span>
@@ -421,51 +614,73 @@
</article> </article>
</div> </div>
<div class="projects-more-row reveal" style="transition-delay:0.22s"> <div class="projects-more-row reveal" style="transition-delay: 0.22s">
<button id="projects-see-more" type="button" class="projects-more-btn" aria-expanded="false">See more</button> <button
id="projects-see-more"
type="button"
class="projects-more-btn"
aria-expanded="false"
>
See more
</button>
</div> </div>
</section> </section>
<div class="divider"></div> <div class="divider"></div>
<!-- ─── Contact ───────────────────────────────────────────────── --> <!-- ─── Contact ───────────────────────────────────────────────── -->
<section id="contact"> <section id="contact">
<p class="section-label reveal">contact</p> <p class="section-label reveal">contact</p>
<div class="contact-inner reveal" style="transition-delay:0.1s"> <div class="contact-inner reveal" style="transition-delay: 0.1s">
<div> <div>
<h2>Let's <em>talk</em></h2> <h2>Let's <em>talk</em></h2>
<p> <p>
I am currently employed and open to the right software engineering opportunity, particularly I am currently employed and open to the right software engineering
backend or full-stack roles with friendly, close-knit teams. opportunity, particularly backend or full-stack roles with friendly,
</p> close-knit teams.
<p>
For professional enquiries, please contact me on LinkedIn.
</p> </p>
<p>For professional enquiries, please contact me on LinkedIn.</p>
</div> </div>
<div class="contact-links"> <div class="contact-links">
<a href="https://github.com/jayo60013" class="contact-link" target="_blank" rel="noopener"> <a
<span class="contact-link-icon contact-link-icon--github" aria-hidden="true"></span> href="https://github.com/jayo60013"
class="contact-link"
target="_blank"
rel="noopener"
>
<span
class="contact-link-icon contact-link-icon--github"
aria-hidden="true"
></span>
<span class="contact-link-label">GitHub</span> <span class="contact-link-label">GitHub</span>
<span class="contact-link-handle">jayo60013</span> <span class="contact-link-handle">jayo60013</span>
</a> </a>
<a href="https://www.linkedin.com/in/jaygatward" class="contact-link" target="_blank" rel="noopener"> <a
<span class="contact-link-icon contact-link-icon--linkedin" aria-hidden="true"></span> href="https://www.linkedin.com/in/jaygatward"
class="contact-link"
target="_blank"
rel="noopener"
>
<span
class="contact-link-icon contact-link-icon--linkedin"
aria-hidden="true"
></span>
<span class="contact-link-label">LinkedIn</span> <span class="contact-link-label">LinkedIn</span>
<span class="contact-link-handle">jaygatward</span> <span class="contact-link-handle">jaygatward</span>
</a> </a>
</div> </div>
</div> </div>
</section> </section>
<!-- ─── Footer ─────────────────────────────────────────────────── --> <!-- ─── Footer ─────────────────────────────────────────────────── -->
<footer> <footer>
<span>umbra.mom - John Gatward</span> <span>umbra.mom - John Gatward</span>
<span> <span>
Source code (selfhosted): <a href="https://gitea.umbra.mom/jay/havox">repository</a> Source code (selfhosted):
<a href="https://gitea.umbra.mom/jay/havox">repository</a>
</span> </span>
</footer> </footer>
<script src="script.js"></script>
</body>
<script src="script.js"></script>
</body>
</html> </html>

View File

@@ -1,15 +1,18 @@
<!DOCTYPE html> <!doctype html>
<html> <html>
<head>
<head> <meta
<meta name="viewport" width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0> name="viewport"
width="device-width,"
initial-scale="1.0,"
maximum-scale="1.0,"
user-scalable="0"
/>
<script src="https://cdn.jsdelivr.net/npm/p5@1.4.0/lib/p5.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/p5@1.4.0/lib/p5.min.js"></script>
<script src="cPoint.js"></script> <script src="cPoint.js"></script>
<script src="cLine.js"></script> <script src="cLine.js"></script>
<script src="sketch.js"></script> <script src="sketch.js"></script>
</head> </head>
<body>
</body>
<body></body>
</html> </html>

View File

@@ -1,6 +1,5 @@
<DOCTYPE! html> <!doctype html>
<html> <html>
<head> <head>
<script src="https://cdn.jsdelivr.net/npm/p5@1.4.0/lib/p5.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/p5@1.4.0/lib/p5.min.js"></script>
</head> </head>
@@ -29,8 +28,14 @@
<h1>Ellipse Construction</h1> <h1>Ellipse Construction</h1>
<p> <p>
Inspired by a Richard Feyman's <a href="https://en.wikipedia.org/wiki/Feynman's_Lost_Lecture">lost Inspired by a Richard Feyman's
lecture</a> & <a href="https://www.youtube.com/watch?v=xdIjYBtnvZU">3blue1brown's video</a>. <a href="https://en.wikipedia.org/wiki/Feynman's_Lost_Lecture"
>lost lecture</a
>
&
<a href="https://www.youtube.com/watch?v=xdIjYBtnvZU"
>3blue1brown's video</a
>.
</p> </p>
<p>A geometric proof as to why planetary orbits are ellipitcal.</p> <p>A geometric proof as to why planetary orbits are ellipitcal.</p>
@@ -38,7 +43,6 @@
<h3 id="percentage"></h3> <h3 id="percentage"></h3>
</div> </div>
<script> <script>
class dot { class dot {
constructor(x, y) { constructor(x, y) {
@@ -63,21 +67,19 @@
stroke(75, 75, 215); stroke(75, 75, 215);
strokeWeight(3); strokeWeight(3);
noFill(); noFill();
ellipse(0, 0, width - (0.0625 * width), height - (0.0625 * height)); ellipse(0, 0, width - 0.0625 * width, height - 0.0625 * height);
stroke(215, 215, 15); stroke(215, 215, 15);
strokeWeight(7); strokeWeight(7);
point(0, 0); point(0, 0);
if (mouseY > 0 && mouseY < height && mouseX > 0 && mouseX < width) { if (mouseY > 0 && mouseY < height && mouseX > 0 && mouseX < width) {
if (mouseIsPressed == true && mouseButton == LEFT) { if (mouseIsPressed == true && mouseButton == LEFT) {
ux = mouseX - width / 2; ux = mouseX - width / 2;
uy = mouseY - height / 2; uy = mouseY - height / 2;
point(ux, uy); point(ux, uy);
getCirclePoints(); getCirclePoints();
drawLines(ux, uy); drawLines(ux, uy);
drawTLines(ux, uy); drawTLines(ux, uy);
} else if (mouseIsPressed == true && mouseButton == RIGHT) { } else if (mouseIsPressed == true && mouseButton == RIGHT) {
ux = mouseX - width / 2; ux = mouseX - width / 2;
uy = mouseY - height / 2; uy = mouseY - height / 2;
@@ -89,7 +91,7 @@
} }
function getCirclePoints() { function getCirclePoints() {
var r = (width - (0.0625 * width)) / 2; var r = (width - 0.0625 * width) / 2;
var step = 1 / numericValue; var step = 1 / numericValue;
var index = 0; var index = 0;
for (var i = 0; i < TWO_PI; i += step) { for (var i = 0; i < TWO_PI; i += step) {
@@ -125,19 +127,27 @@
var cy = (y1 + y2) / 2; var cy = (y1 + y2) / 2;
//move line to the center on the origin //move line to the center on the origin
x1 -= cx; y1 -= cy; x1 -= cx;
x2 -= cx; y2 -= cy; y1 -= cy;
x2 -= cx;
y2 -= cy;
//rotate both points //rotate both points
xtemp = x1; ytemp = y1; xtemp = x1;
x1 = -ytemp; y1 = xtemp; ytemp = y1;
x1 = -ytemp;
y1 = xtemp;
xtemp = x2; ytemp = y2; xtemp = x2;
x2 = -ytemp; y2 = xtemp; ytemp = y2;
x2 = -ytemp;
y2 = xtemp;
//move the center point back to where it was //move the center point back to where it was
x1 += cx; y1 += cy; x1 += cx;
x2 += cx; y2 += cy; y1 += cy;
x2 += cx;
y2 += cy;
stroke(255, 0, 0); stroke(255, 0, 0);
line(x1, y1, x2, y2); line(x1, y1, x2, y2);
stroke(255); stroke(255);
@@ -148,11 +158,10 @@
do { do {
ux = random(width); ux = random(width);
uy = random(height); uy = random(height);
if (getDist(0, ux, 0, uy) <= (width - (0.0625 * width)) / 2) { if (getDist(0, ux, 0, uy) <= (width - 0.0625 * width) / 2) {
pointOk = true; pointOk = true;
} }
} } while (!pointOk);
while (!pointOk);
point(ux, uy); point(ux, uy);
getCirclePoints(); getCirclePoints();
@@ -165,11 +174,10 @@
do { do {
ux = random(width); ux = random(width);
uy = random(height); uy = random(height);
if (getDist(0, ux, 0, uy) <= (width - (0.0625 * width)) / 2) { if (getDist(0, ux, 0, uy) <= (width - 0.0625 * width) / 2) {
pointOk = true; pointOk = true;
} }
} } while (!pointOk);
while (!pointOk);
point(ux, uy); point(ux, uy);
getCirclePoints(); getCirclePoints();
@@ -177,8 +185,7 @@
} }
function getDist(x1, x2, y1, y2) { function getDist(x1, x2, y1, y2) {
return Math.sqrt((x1 - x2) ^ 2 + (y1 - y2) ^ 2); return Math.sqrt((x1 - x2) ^ (2 + (y1 - y2)) ^ 2);
} }
</script> </script>
</html>
</html>

View File

@@ -1,23 +1,38 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Flocking Boids - WASM</title> <title>Flocking Boids - WASM</title>
<link rel="stylesheet" href="style.css"> <link rel="stylesheet" href="style.css" />
</head> </head>
<body> <body>
<div id="container"> <div id="container">
<canvas id="canvas"></canvas> <canvas id="canvas"></canvas>
<aside id="controls" aria-expanded="true"> <aside id="controls" aria-expanded="true">
<div id="controlsHeader"> <div id="controlsHeader">
<h2 id="controlsTitle">Flocking Controls</h2> <h2 id="controlsTitle">Flocking Controls</h2>
<button id="controlsToggle" type="button" aria-controls="controlsBody" aria-expanded="true" aria-label="Collapse controls"></button> <button
id="controlsToggle"
type="button"
aria-controls="controlsBody"
aria-expanded="true"
aria-label="Collapse controls"
>
</button>
</div> </div>
<div id="controlsBody"> <div id="controlsBody">
<div class="control-group"> <div class="control-group">
<label for="alignSlider">Alignment</label> <label for="alignSlider">Alignment</label>
<input type="range" id="alignSlider" min="0" max="5" step="0.1" value="1.0"> <input
type="range"
id="alignSlider"
min="0"
max="5"
step="0.1"
value="1.0"
/>
<div class="value-display"> <div class="value-display">
<span class="stat-label">Weight:</span> <span class="stat-label">Weight:</span>
<strong id="alignValue">1.00</strong> <strong id="alignValue">1.00</strong>
@@ -26,7 +41,14 @@
<div class="control-group"> <div class="control-group">
<label for="cohesionSlider">Cohesion</label> <label for="cohesionSlider">Cohesion</label>
<input type="range" id="cohesionSlider" min="0" max="5" step="0.1" value="0.9"> <input
type="range"
id="cohesionSlider"
min="0"
max="5"
step="0.1"
value="0.9"
/>
<div class="value-display"> <div class="value-display">
<span class="stat-label">Weight:</span> <span class="stat-label">Weight:</span>
<strong id="cohesionValue">0.90</strong> <strong id="cohesionValue">0.90</strong>
@@ -35,7 +57,14 @@
<div class="control-group"> <div class="control-group">
<label for="separationSlider">Separation</label> <label for="separationSlider">Separation</label>
<input type="range" id="separationSlider" min="0" max="5" step="0.1" value="1.4"> <input
type="range"
id="separationSlider"
min="0"
max="5"
step="0.1"
value="1.4"
/>
<div class="value-display"> <div class="value-display">
<span class="stat-label">Weight:</span> <span class="stat-label">Weight:</span>
<strong id="separationValue">1.40</strong> <strong id="separationValue">1.40</strong>
@@ -73,6 +102,5 @@
</div> </div>
<script type="module" src="script.js"></script> <script type="module" src="script.js"></script>
</body> </body>
</html> </html>

View File

@@ -1,7 +1,6 @@
<!DOCTYPE html> <!doctype html>
<html> <html>
<head>
<head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<title>Fourier Series — Epicycles (p5.js)</title> <title>Fourier Series — Epicycles (p5.js)</title>
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
@@ -31,20 +30,20 @@
width: 260px; width: 260px;
} }
</style> </style>
</head> </head>
<body>
<body>
<div id="container"> <div id="container">
<h1>Fourier Series</h1> <h1>Fourier Series</h1>
<p> <p>
This interactive sketch visualizes a Fourier series using epicycles. Each circle represents This interactive sketch visualizes a Fourier series using epicycles.
an odd harmonic; as you add more epicycles with the slider, the path on the right converges toward a Each circle represents an odd harmonic; as you add more epicycles with
square wave. The rotating vectors sum to a point whose vertical position is traced over time. the slider, the path on the right converges toward a square wave. The
rotating vectors sum to a point whose vertical position is traced over
time.
</p> </p>
<div class="canvas-holder"> <div class="canvas-holder"></div>
</div>
</div> </div>
<script> <script>
@@ -117,6 +116,5 @@
} }
} }
</script> </script>
</body> </body>
</html> </html>

View File

@@ -1,13 +1,25 @@
<!DOCTYPE html> <!doctype html>
<html> <html>
<head>
<head> <meta
<meta name="viewport" width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0> name="viewport"
width="device-width,"
initial-scale="1.0,"
maximum-scale="1.0,"
user-scalable="0"
/>
<script src="https://cdn.jsdelivr.net/npm/p5@1.4.0/lib/p5.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/p5@1.4.0/lib/p5.min.js"></script>
<style> <style>
body { body {
font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif; font-family:
system-ui,
-apple-system,
Segoe UI,
Roboto,
Helvetica,
Arial,
sans-serif;
margin: 16px; margin: 16px;
background: #fafafa; background: #fafafa;
color: #222; color: #222;
@@ -34,23 +46,21 @@
margin: 0 auto; margin: 0 auto;
} }
</style> </style>
</head>
</head> <body>
<body>
<div class="intro"> <div class="intro">
<h1>Marching Squares Field Visualizer</h1> <h1>Marching Squares Field Visualizer</h1>
<p> <p>
This sketch uses the <strong>Marching Squares</strong> algorithm to generate and display contour boundaries This sketch uses the <strong>Marching Squares</strong> algorithm to
on a grid. Each cell is either active or inactive, the algorithm determines the correct line or triangle to generate and display contour boundaries on a grid. Each cell is either
draw based on the surrounding corners. active or inactive, the algorithm determines the correct line or
triangle to draw based on the surrounding corners.
</p> </p>
<p><b>Left-click</b> toggles a cell between the two states.</p>
<p> <p>
<b>Left-click</b> toggles a cell between the two states. <b>Rightclick</b> shows or hides the contour lines produced by the
</p> algorithm.
<p>
<b>Rightclick</b> shows or hides the contour lines produced by the algorithm.
</p> </p>
</div> </div>
@@ -68,8 +78,7 @@
cols = width / reso + 1; cols = width / reso + 1;
rows = height / reso + 1; rows = height / reso + 1;
for (var i = 0; i < cols * rows; i++) for (var i = 0; i < cols * rows; i++) field[i] = Math.floor(random(2));
field[i] = Math.floor(random(2));
} }
function draw() { function draw() {
@@ -88,17 +97,24 @@
} }
for (var i = 0; i < cols * rows; i++) { for (var i = 0; i < cols * rows; i++) {
if (i + cols + 1 > cols * rows) {break;} if (i + cols + 1 > cols * rows) {
break;
}
var x = (i % cols) * reso; var x = (i % cols) * reso;
var y = (Math.floor(i / cols)) * reso; var y = Math.floor(i / cols) * reso;
const a = [x + reso * 0.5, y]; const a = [x + reso * 0.5, y];
const b = [x + reso, y + reso * 0.5]; const b = [x + reso, y + reso * 0.5];
const c = [x + reso * 0.5, y + reso]; const c = [x + reso * 0.5, y + reso];
const d = [x, y + reso * 0.5]; const d = [x, y + reso * 0.5];
var s = getState(field[i], field[i + 1], field[i + cols + 1], field[i + cols]); var s = getState(
field[i],
field[i + 1],
field[i + cols + 1],
field[i + cols],
);
noStroke(); noStroke();
switch (s) { switch (s) {
@@ -162,8 +178,10 @@
break; break;
default: default:
break; break;
}; }
if (draw_lines) {drawLines(s, a, b, c, d);} if (draw_lines) {
drawLines(s, a, b, c, d);
}
} }
} }
@@ -175,26 +193,32 @@
stroke(232, 229, 55); stroke(232, 229, 55);
strokeWeight(reso * 0.1); strokeWeight(reso * 0.1);
switch (s) { switch (s) {
case 1: case 14: case 1:
case 14:
line(c[0], c[1], d[0], d[1]); line(c[0], c[1], d[0], d[1]);
break; break;
case 2: case 13: case 2:
case 13:
line(b[0], b[1], c[0], c[1]); line(b[0], b[1], c[0], c[1]);
break; break;
case 3: case 12: case 3:
case 12:
line(b[0], b[1], d[0], d[1]); line(b[0], b[1], d[0], d[1]);
break; break;
case 4: case 11: case 4:
case 11:
line(a[0], a[1], b[0], b[1]); line(a[0], a[1], b[0], b[1]);
break; break;
case 5: case 5:
line(a[0], a[1], d[0], d[1]); line(a[0], a[1], d[0], d[1]);
line(b[0], b[1], c[0], c[1]); line(b[0], b[1], c[0], c[1]);
break; break;
case 6: case 9: case 6:
case 9:
line(a[0], a[1], c[0], c[1]); line(a[0], a[1], c[0], c[1]);
break; break;
case 7: case 8: case 7:
case 8:
line(a[0], a[1], d[0], d[1]); line(a[0], a[1], d[0], d[1]);
break; break;
case 10: case 10:
@@ -203,7 +227,7 @@
break; break;
default: default:
break; break;
}; }
noStroke(); noStroke();
} }
@@ -219,6 +243,5 @@
} }
} }
</script> </script>
</body> </body>
</html> </html>

View File

@@ -1,7 +1,12 @@
<html> <html>
<head>
<head> <meta
<meta name="viewport" width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0> name="viewport"
width="device-width,"
initial-scale="1.0,"
maximum-scale="1.0,"
user-scalable="0"
/>
<style> <style>
body { body {
padding: 0; padding: 0;
@@ -32,21 +37,16 @@
margin: 10px 0; margin: 10px 0;
} }
</style> </style>
</head> </head>
<div id="container">
<div id="container">
<h1>Maze Generator</h1> <h1>Maze Generator</h1>
<p> <p>Toy maze generator using DFS & recursice back tracking.</p>
Toy maze generator using DFS & recursice back tracking.
</p>
<h2 id="PIbox"></h2> <h2 id="PIbox"></h2>
<h3 id="percentage"></h3> <h3 id="percentage"></h3>
</div> </div>
<body>
</body>
<body></body>
</html> </html>

View File

@@ -1,12 +1,12 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Percolation</title> <title>Percolation</title>
<link rel="stylesheet" href="style.css"/> <link rel="stylesheet" href="style.css" />
</head> </head>
<body> <body>
<div class="container"> <div class="container">
<a href="/#projects" class="back-button">← Back</a> <a href="/#projects" class="back-button">← Back</a>
@@ -17,15 +17,18 @@
<section> <section>
<p> <p>
<strong>Percolation</strong> is a simple model for connectivity on a grid. Each cell is either open or closed. <strong>Percolation</strong> is a simple model for connectivity on a
This simulation stops as soon as there is a path from the top of the grid to the bottom through the open cells. grid. Each cell is either open or closed. This simulation stops as
soon as there is a path from the top of the grid to the bottom through
the open cells.
</p> </p>
<div class="info-box"> <div class="info-box">
<p> <p>
In this simulation, cells are randomly opened on a grid. Percolation occurs when there exists In this simulation, cells are randomly opened on a grid. Percolation
a connected path of open cells from the top to the bottom of the grid. It is a neat way to visualise occurs when there exists a connected path of open cells from the top
threshold behaviour: below a certain probability nothing connects, and above it large connected regions to the bottom of the grid. It is a neat way to visualise threshold
suddenly begin to appear. behaviour: below a certain probability nothing connects, and above
it large connected regions suddenly begin to appear.
</p> </p>
</div> </div>
</section> </section>
@@ -33,11 +36,17 @@
<div class="canvas-container"> <div class="canvas-container">
<div class="canvas-toolbar"> <div class="canvas-toolbar">
<p>Canvas</p> <p>Canvas</p>
<button id="fullscreenButton" class="action-button" type="button">Fullscreen</button> <button id="fullscreenButton" class="action-button" type="button">
Fullscreen
</button>
</div> </div>
<div class="canvas-shell"> <div class="canvas-shell">
<canvas id="canvas" aria-label="Percolation simulation canvas" oncontextmenu="event.preventDefault()"></canvas> <canvas
id="canvas"
aria-label="Percolation simulation canvas"
oncontextmenu="event.preventDefault()"
></canvas>
</div> </div>
<div id="status" class="status">Downloading...</div> <div id="status" class="status">Downloading...</div>
@@ -53,13 +62,13 @@
<section> <section>
<h3>Technical Details</h3> <h3>Technical Details</h3>
<p> <p>
The simulation uses a <strong>disjoint-set (Union-Find)</strong> style connectivity algorithm. As random cells open, The simulation uses a <strong>disjoint-set (Union-Find)</strong> style
each open cell is union-ed with its open neighbours, and two virtual nodes represent the top and bottom edges connectivity algorithm. As random cells open, each open cell is
of the grid. Percolation is detected the moment those two virtual nodes become connected. union-ed with its open neighbours, and two virtual nodes represent the
</p> top and bottom edges of the grid. Percolation is detected the moment
<p> those two virtual nodes become connected.
This makes each update fast and scalable even for larger grids.
</p> </p>
<p>This makes each update fast and scalable even for larger grids.</p>
</section> </section>
<footer> <footer>
@@ -69,5 +78,5 @@
<script src="script.js"></script> <script src="script.js"></script>
<script async src="index.js"></script> <script async src="index.js"></script>
</body> </body>
</html> </html>

View File

@@ -1,12 +1,11 @@
<!DOCTYPE html> <!doctype html>
<html> <html>
<script src="https://cdn.jsdelivr.net/npm/p5@1.4.0/lib/p5.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/p5@1.4.0/lib/p5.min.js"></script>
<h1 id="PIbox"></h1>
<h2 id="percentage"></h2>
<h1 id="PIbox"></h1> <style>
<h2 id="percentage"></h2>
<style>
body { body {
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
background: #fafafa; background: #fafafa;
@@ -24,25 +23,23 @@
p { p {
margin: 10px 0; margin: 10px 0;
} }
</style> </style>
<div id="container">
<div id="container">
<h1>Monte Carlo π Estimator</h1> <h1>Monte Carlo π Estimator</h1>
<p> <p>
A small demonstation on how the Monte Carlo method can approximate the value of π A small demonstation on how the Monte Carlo method can approximate the
by randomly placing points inside a square and checking how many fall within value of π by randomly placing points inside a square and checking how
the inner circle. The closer the ratio gets to π/4, the more accurate many fall within the inner circle. The closer the ratio gets to π/4, the
the estimation becomes. more accurate the estimation becomes.
</p> </p>
<h2 id="PIbox"></h2> <h2 id="PIbox"></h2>
<h3 id="percentage"></h3> <h3 id="percentage"></h3>
</div> </div>
<script>
<script>
const r = 400; const r = 400;
var circleCount, total; var circleCount, total;
var x, y; var x, y;
@@ -56,7 +53,7 @@
background(0); background(0);
stroke(255, 0, 0); stroke(255, 0, 0);
strokeWeight(3); strokeWeight(3);
noFill() noFill();
translate(width / 2, height / 2); translate(width / 2, height / 2);
rectMode(CENTER); rectMode(CENTER);
rect(0, 0, r * 2, r * 2); rect(0, 0, r * 2, r * 2);
@@ -66,19 +63,18 @@
} }
function draw() { function draw() {
translate(width / 2, height / 2); translate(width / 2, height / 2);
x = random(-r, r) x = random(-r, r);
y = random(-r, r) y = random(-r, r);
stroke(0, 255, 0) stroke(0, 255, 0);
total += 1 total += 1;
var d = dist(x, y, 0, 0) var d = dist(x, y, 0, 0);
if (d < r) { if (d < r) {
circleCount += 1 circleCount += 1;
stroke(0, 0, 255) stroke(0, 0, 255);
} }
point(x, y); point(x, y);
@@ -93,12 +89,11 @@
var ErrorDisplay = document.getElementById("percentage"); var ErrorDisplay = document.getElementById("percentage");
var DisplayError = "a"; var DisplayError = "a";
var Perror = (calcPI - PI) / PI var Perror = (calcPI - PI) / PI;
Perror *= 100; Perror *= 100;
DisplayError = nfc(Perror, 10); DisplayError = nfc(Perror, 10);
ErrorDisplay.innerHTML = DisplayError + '%'; ErrorDisplay.innerHTML = DisplayError + "%";
} }
} }
</script> </script>
</html> </html>

View File

@@ -1,7 +1,7 @@
<!DOCTYPE html> <!doctype html>
<html> <html>
<script src="https://cdn.jsdelivr.net/npm/p5@1.4.0/lib/p5.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/p5@1.4.0/lib/p5.min.js"></script>
<script> <script>
var r = 10; //circle size var r = 10; //circle size
var k = 30; var k = 30;
var grid = []; var grid = [];
@@ -16,14 +16,12 @@
strokeWeight(4); strokeWeight(4);
colorMode(HSB); colorMode(HSB);
cols = floor(width / w); cols = floor(width / w);
rows = floor(height / w); rows = floor(height / w);
for (var i = 0; i < cols * rows; i++) { for (var i = 0; i < cols * rows; i++) {
grid[i] = undefined; grid[i] = undefined;
} }
var x = width / 2; var x = width / 2;
var y = height / 2; var y = height / 2;
var i = floor(x / w); var i = floor(x / w);
@@ -31,13 +29,11 @@
var pos = createVector(x, y); var pos = createVector(x, y);
grid[i + j * cols] = pos; grid[i + j * cols] = pos;
active.push(pos); active.push(pos);
} }
function draw() { function draw() {
background(0); background(0);
for (var total = 0; total < 30; total++) { for (var total = 0; total < 30; total++) {
if (active.length > 0) { if (active.length > 0) {
var randIndex = floor(random(active.length)); var randIndex = floor(random(active.length));
@@ -52,11 +48,17 @@
var col = floor(sample.x / w); var col = floor(sample.x / w);
var row = floor(sample.y / w); var row = floor(sample.y / w);
if (col > -1 && row > -1 && col < cols && row < rows && !grid[col + row * cols]) { if (
col > -1 &&
row > -1 &&
col < cols &&
row < rows &&
!grid[col + row * cols]
) {
var flag = true; var flag = true;
for (var i = -1; i <= 1; i++) { for (var i = -1; i <= 1; i++) {
for (var j = -1; j <= 1; j++) { for (var j = -1; j <= 1; j++) {
var index = (col + i) + (row + j) * cols; var index = col + i + (row + j) * cols;
var neighbor = grid[index]; var neighbor = grid[index];
if (neighbor) { if (neighbor) {
var d = p5.Vector.dist(sample, neighbor); var d = p5.Vector.dist(sample, neighbor);
@@ -80,12 +82,10 @@
if (!found) { if (!found) {
active.splice(randIndex, 1); active.splice(randIndex, 1);
} }
} }
} }
for (var i = 0; i < ordered.length; i++) { for (var i = 0; i < ordered.length; i++) {
stroke(255, 0, 255); stroke(255, 0, 255);
strokeWeight(r * 0.5); strokeWeight(r * 0.5);
point(ordered[i].x, ordered[i].y); point(ordered[i].x, ordered[i].y);
@@ -99,6 +99,5 @@
} }
} }
} }
</script> </script>
</html> </html>

View File

@@ -1,13 +1,16 @@
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="Thin Ice - a cute pixel art winter puzzle game." /> <meta
name="description"
content="Thin Ice - a cute pixel art winter puzzle game."
/>
<title>Thin Ice | havox</title> <title>Thin Ice | havox</title>
<link rel="stylesheet" href="style.css" /> <link rel="stylesheet" href="style.css" />
</head> </head>
<body> <body>
<div class="container"> <div class="container">
<a href="/#projects" class="back-button">← Back</a> <a href="/#projects" class="back-button">← Back</a>
@@ -18,11 +21,13 @@
<section> <section>
<p> <p>
You start on safe ice, breaking ice where you walk, trying to reach the finish tile before trapping yourself. You start on safe ice, breaking ice where you walk, trying to reach
the finish tile before trapping yourself.
</p> </p>
<div class="info-box"> <div class="info-box">
<p> <p>
See if you can get a gold medal on all 6 levels by breaking every single tile of ice! See if you can get a gold medal on all 6 levels by breaking every
single tile of ice!
</p> </p>
</div> </div>
</section> </section>
@@ -30,16 +35,30 @@
<div class="canvas-container"> <div class="canvas-container">
<div class="canvas-toolbar"> <div class="canvas-toolbar">
<p>Game</p> <p>Game</p>
<button id="fullscreenButton" class="action-button" type="button">Fullscreen</button> <button id="fullscreenButton" class="action-button" type="button">
Fullscreen
</button>
</div> </div>
<div class="canvas-shell"> <div class="canvas-shell">
<canvas id="canvas" class="emscripten" aria-label="Thin Ice game canvas" oncontextmenu="event.preventDefault()" tabindex="-1"></canvas> <canvas
id="canvas"
class="emscripten"
aria-label="Thin Ice game canvas"
oncontextmenu="event.preventDefault()"
tabindex="-1"
></canvas>
</div> </div>
<div class="canvas-options"> <div class="canvas-options">
<label><input type="checkbox" id="resize" /> Resize canvas in fullscreen</label> <label
<label><input type="checkbox" id="pointerLock" checked /> Lock pointer in fullscreen</label> ><input type="checkbox" id="resize" /> Resize canvas in
fullscreen</label
>
<label
><input type="checkbox" id="pointerLock" checked /> Lock pointer in
fullscreen</label
>
</div> </div>
<div id="status" class="status">Downloading...</div> <div id="status" class="status">Downloading...</div>
@@ -68,5 +87,5 @@
<script src="script.js"></script> <script src="script.js"></script>
<script async src="thin_ice.js"></script> <script async src="thin_ice.js"></script>
</body> </body>
</html> </html>

View File

@@ -1,32 +1,44 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Travelling Salesman Problem</title> <title>Travelling Salesman Problem</title>
<link rel="stylesheet" href="./style.css" /> <link rel="stylesheet" href="./style.css" />
</head> </head>
<body> <body>
<div class="container"> <div class="container">
<a href="/#projects" class="back-button">← Back</a> <a href="/#projects" class="back-button">← Back</a>
<header> <header>
<h1>Travelling Salesman Problem</h1> <h1>Travelling Salesman Problem</h1>
<p class="subtitle">An interactive route visualiser built with WebGL and WebAssembly.</p> <p class="subtitle">
An interactive route visualiser built with WebGL and WebAssembly.
</p>
</header> </header>
<section> <section>
<p> <p>
After writing one of my first tutorials on the Travelling Salesman Problem, I wanted to revisit it with a slightly more technical stack. After writing one of my first tutorials on the Travelling Salesman
The backend is written in Zig (compiled to WebAssembly), and the rendering is done directly in WebGL instead of p5.js or Raylib. Problem, I wanted to revisit it with a slightly more technical stack.
The backend is written in Zig (compiled to WebAssembly), and the
rendering is done directly in WebGL instead of p5.js or Raylib.
</p> </p>
<p> <p>
This project was also inspired by <a href="https://www.youtube.com/@sphaerophoria">sphaerophoria</a> who started a <a href="https://www.youtube.com/watch?v=RiLjPBci6Og&list=PL980gcR1LE3L8RoIMSNBFfw4dFfS3rrsk">series</a> on creating his own map. This project was also inspired by
<a href="https://www.youtube.com/@sphaerophoria">sphaerophoria</a> who
started a
<a
href="https://www.youtube.com/watch?v=RiLjPBci6Og&list=PL980gcR1LE3L8RoIMSNBFfw4dFfS3rrsk"
>series</a
>
on creating his own map.
</p> </p>
<div class="info-box"> <div class="info-box">
<p> <p>
Drag points around the canvas and watch the route update in real time. Drag points around the canvas and watch the route update in real
time.
</p> </p>
</div> </div>
</section> </section>
@@ -35,31 +47,52 @@
<div class="canvas-toolbar"> <div class="canvas-toolbar">
<div class="control-group" aria-label="TSP controls"> <div class="control-group" aria-label="TSP controls">
<label for="pointCountInput">Points</label> <label for="pointCountInput">Points</label>
<input id="pointCountInput" type="number" min="5" max="100" step="1" value="36" /> <input
<button id="applyPointCountButton" class="action-button" type="button">Regenerate</button> id="pointCountInput"
type="number"
min="5"
max="100"
step="1"
value="36"
/>
<button
id="applyPointCountButton"
class="action-button"
type="button"
>
Regenerate
</button>
</div> </div>
</div> </div>
<div class="canvas-shell"> <div class="canvas-shell">
<canvas id="tsp-canvas" aria-label="Travelling Salesman Problem canvas"> <canvas
id="tsp-canvas"
aria-label="Travelling Salesman Problem canvas"
>
HTML5 canvas not supported in browser HTML5 canvas not supported in browser
</canvas> </canvas>
</div> </div>
<div class="status-row"> <div class="status-row">
<div id="pointCountStatus" class="status">36 points</div> <div id="pointCountStatus" class="status">36 points</div>
<p class="canvas-note">Tip: click and drag a point to recompute the route.</p> <p class="canvas-note">
Tip: click and drag a point to recompute the route.
</p>
</div> </div>
</div> </div>
<section> <section>
<h3>Technical Details</h3> <h3>Technical Details</h3>
<p> <p>
The backend is written in Zig, and the route is built in two passes: a nearest-neighbour pass followed by a 2-opt local search. The backend is written in Zig, and the route is built in two passes: a
nearest-neighbour pass followed by a 2-opt local search.
</p> </p>
<p> <p>
Point coordinates are stored in WebAssembly memory, and the route order is recalculated whenever the layout changes. Point coordinates are stored in WebAssembly memory, and the route
Rendering is handled in WebGL to keep interaction smooth as the point count increases. order is recalculated whenever the layout changes. Rendering is
handled in WebGL to keep interaction smooth as the point count
increases.
</p> </p>
</section> </section>
@@ -70,5 +103,5 @@
<script src="./point.js"></script> <script src="./point.js"></script>
<script type="module" src="./main.js"></script> <script type="module" src="./main.js"></script>
</body> </body>
</html> </html>

View File

@@ -199,4 +199,3 @@ footer {
height: 400px; height: 400px;
} }
} }

View File

@@ -1,15 +1,27 @@
<!DOCTYPE html> <!doctype html>
<html> <html>
<head>
<head> <meta
<meta name="viewport" width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0> name="viewport"
width="device-width,"
initial-scale="1.0,"
maximum-scale="1.0,"
user-scalable="0"
/>
<script src="https://cdn.jsdelivr.net/npm/p5@1.4.0/lib/p5.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/p5@1.4.0/lib/p5.min.js"></script>
<style> <style>
body { body {
padding: 0; padding: 0;
margin: 0; margin: 0;
font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif; font-family:
system-ui,
-apple-system,
Segoe UI,
Roboto,
Helvetica,
Arial,
sans-serif;
background: #fafafa; background: #fafafa;
} }
@@ -49,9 +61,9 @@
/* Remove outline */ /* Remove outline */
opacity: 1; opacity: 1;
/* Set transparency (for mouse-over effects on hover) */ /* Set transparency (for mouse-over effects on hover) */
-webkit-transition: .2s; -webkit-transition: 0.2s;
/* 0.2 seconds transition on hover */ /* 0.2 seconds transition on hover */
transition: opacity .2s; transition: opacity 0.2s;
} }
.slider::-webkit-slider-thumb { .slider::-webkit-slider-thumb {
@@ -79,25 +91,36 @@
/* Cursor on hover */ /* Cursor on hover */
} }
</style> </style>
</head> </head>
<body>
<body>
<div class="intro"> <div class="intro">
<h1>Oscillating Arrow Field Visualizer</h1> <h1>Oscillating Arrow Field Visualizer</h1>
<p> <p>
A sketch to <s>hopefully</s> demonstrate the illusion of the <a A sketch to <s>hopefully</s> demonstrate the illusion of the
href="https://en.wikipedia.org/wiki/M%C3%BCller-Lyer_illusion">Müller-Lyer Illusion</a> <a href="https://en.wikipedia.org/wiki/M%C3%BCller-Lyer_illusion"
</p> >Müller-Lyer Illusion</a
<p>The lines - that are the same length, should look longer and shorter based on the angle of the arrow heads. >
</p> </p>
<p> <p>
Increasing the value adds more horizontal slices, creating a denser and more detailed pattern. The lines - that are the same length, should look longer and shorter
Tap or click to pause and resume the motion. based on the angle of the arrow heads.
</p>
<p>
Increasing the value adds more horizontal slices, creating a denser and
more detailed pattern. Tap or click to pause and resume the motion.
</p> </p>
</div> </div>
<div class="slidecontainer"> <div class="slidecontainer">
<input type="range" min="2" max="11" step="1" value="2" class="slider" id="myRange"> <input
type="range"
min="2"
max="11"
step="1"
value="2"
class="slider"
id="myRange"
/>
</div> </div>
<script> <script>
@@ -119,7 +142,7 @@
function draw() { function draw() {
background(43); background(43);
angleMode(DEGREES); angleMode(DEGREES);
lineNum = document.getElementById('myRange').value; lineNum = document.getElementById("myRange").value;
drawLine(hMargin, lineNum + 1); drawLine(hMargin, lineNum + 1);
@@ -137,23 +160,57 @@
let changex = sin(t) * 18; let changex = sin(t) * 18;
vm /= 10; vm /= 10;
for (let j = 1; j < vm - 1; j++) { for (let j = 1; j < vm - 1; j++) {
stroke(255, 34, 63); stroke(255, 34, 63);
strokeWeight(4); strokeWeight(4);
line(width / hm, j * height / vm, width / 2, j * height / vm); //red line line(width / hm, (j * height) / vm, width / 2, (j * height) / vm); //red line
stroke(69, 98, 255); stroke(69, 98, 255);
line(width / 2, j * height / vm, (hm - 1) * width / hm, j * height / vm); //blue line line(
width / 2,
(j * height) / vm,
((hm - 1) * width) / hm,
(j * height) / vm,
); //blue line
stroke(255); stroke(255);
line(width / hm, j * height / vm, width / hm + changex, (j * height / vm) - arrowHeight); //left top line(
line(width / hm + changex, (j * height / vm) + arrowHeight, width / hm, j * height / vm); //left bottom width / hm,
(j * height) / vm,
width / hm + changex,
(j * height) / vm - arrowHeight,
); //left top
line(
width / hm + changex,
(j * height) / vm + arrowHeight,
width / hm,
(j * height) / vm,
); //left bottom
line((hm - 1) * width / hm + changex, j * height / vm + arrowHeight, (hm - 1) * width / hm, j * height / vm); //right bottom line(
line(width / 2 - changex, j * height / vm + arrowHeight, width / 2, j * height / vm); //center bottom ((hm - 1) * width) / hm + changex,
(j * height) / vm + arrowHeight,
((hm - 1) * width) / hm,
(j * height) / vm,
); //right bottom
line(
width / 2 - changex,
(j * height) / vm + arrowHeight,
width / 2,
(j * height) / vm,
); //center bottom
line((hm - 1) * width / hm, j * height / vm, (hm - 1) * width / hm + changex, (j * height / vm) - arrowHeight); //right bottom line(
line(width / 2, j * height / vm, width / 2 - changex, (j * height / vm) - arrowHeight); //center top ((hm - 1) * width) / hm,
(j * height) / vm,
((hm - 1) * width) / hm + changex,
(j * height) / vm - arrowHeight,
); //right bottom
line(
width / 2,
(j * height) / vm,
width / 2 - changex,
(j * height) / vm - arrowHeight,
); //center top
} }
} }
@@ -161,6 +218,5 @@
lineMove = !lineMove; lineMove = !lineMove;
} }
</script> </script>
</body> </body>
</html> </html>

116
style.css
View File

@@ -28,7 +28,9 @@
--rosewater: #f5e0dc; --rosewater: #f5e0dc;
} }
*, *::before, *::after { *,
*::before,
*::after {
box-sizing: border-box; box-sizing: border-box;
margin: 0; margin: 0;
padding: 0; padding: 0;
@@ -39,15 +41,34 @@ html {
} }
body { body {
background: radial-gradient(rgba(205, 214, 244, 0.028) 0.5px, transparent 0.6px), background:
radial-gradient(rgba(205, 214, 244, 0.028) 0.5px, transparent 0.6px),
radial-gradient(rgba(17, 17, 27, 0.032) 0.5px, transparent 0.6px), radial-gradient(rgba(17, 17, 27, 0.032) 0.5px, transparent 0.6px),
radial-gradient(1200px 800px at 12% 8%, rgba(137, 180, 250, 0.07), transparent 62%), radial-gradient(
radial-gradient(1000px 720px at 88% 92%, rgba(203, 166, 247, 0.07), transparent 64%), 1200px 800px at 12% 8%,
rgba(137, 180, 250, 0.07),
transparent 62%
),
radial-gradient(
1000px 720px at 88% 92%,
rgba(203, 166, 247, 0.07),
transparent 64%
),
linear-gradient(180deg, #1e1e2e 0%, #1b1b2b 55%, #1a1a29 100%); linear-gradient(180deg, #1e1e2e 0%, #1b1b2b 55%, #1a1a29 100%);
background-size: 3px 3px, 3px 3px, auto, auto, auto; background-size:
background-position: 0 0, 1px 1px, 0 0, 0 0, 0 0; 3px 3px,
3px 3px,
auto,
auto,
auto;
background-position:
0 0,
1px 1px,
0 0,
0 0,
0 0;
color: var(--text); color: var(--text);
font-family: 'JetBrains Mono', 'Apple Color Emoji', monospace; font-family: "JetBrains Mono", "Apple Color Emoji", monospace;
font-size: 15px; font-size: 15px;
line-height: 1.7; line-height: 1.7;
overflow-x: hidden; overflow-x: hidden;
@@ -56,7 +77,7 @@ body {
body::before, body::before,
body::after { body::after {
content: ''; content: "";
position: fixed; position: fixed;
width: 52rem; width: 52rem;
height: 42rem; height: 42rem;
@@ -71,14 +92,22 @@ body::before {
top: -18rem; top: -18rem;
right: -16rem; right: -16rem;
transform: rotate(14deg); transform: rotate(14deg);
background: radial-gradient(closest-side at 38% 42%, rgba(203, 166, 247, 0.42), transparent 72%); background: radial-gradient(
closest-side at 38% 42%,
rgba(203, 166, 247, 0.42),
transparent 72%
);
} }
body::after { body::after {
bottom: -20rem; bottom: -20rem;
left: -18rem; left: -18rem;
transform: rotate(-12deg); transform: rotate(-12deg);
background: radial-gradient(closest-side at 60% 58%, rgba(137, 180, 250, 0.36), transparent 72%); background: radial-gradient(
closest-side at 60% 58%,
rgba(137, 180, 250, 0.36),
transparent 72%
);
} }
::selection { ::selection {
@@ -122,7 +151,7 @@ nav {
} }
.nav-logo { .nav-logo {
font-family: 'Fraunces', serif; font-family: "Fraunces", serif;
font-size: 1.3rem; font-size: 1.3rem;
color: var(--mauve); color: var(--mauve);
text-decoration: none; text-decoration: none;
@@ -146,7 +175,7 @@ nav {
} }
.nav-links a::after { .nav-links a::after {
content: ''; content: "";
position: absolute; position: absolute;
bottom: -3px; bottom: -3px;
left: 0; left: 0;
@@ -193,7 +222,7 @@ section.full-width {
} }
.section-label::after { .section-label::after {
content: ''; content: "";
display: block; display: block;
height: 1px; height: 1px;
width: 3rem; width: 3rem;
@@ -222,7 +251,7 @@ section.full-width {
} }
.hero-name { .hero-name {
font-family: 'Fraunces', serif; font-family: "Fraunces", serif;
font-size: clamp(3.5rem, 9vw, 7.5rem); font-size: clamp(3.5rem, 9vw, 7.5rem);
font-weight: 600; font-weight: 600;
line-height: 1; line-height: 1;
@@ -238,7 +267,7 @@ section.full-width {
} }
.hero-subtitle { .hero-subtitle {
font-family: 'Fraunces', serif; font-family: "Fraunces", serif;
font-size: clamp(1.2rem, 3vw, 2.2rem); font-size: clamp(1.2rem, 3vw, 2.2rem);
font-weight: 300; font-weight: 300;
color: var(--subtext1); color: var(--subtext1);
@@ -270,7 +299,7 @@ section.full-width {
} }
.hero-by-role { .hero-by-role {
font-family: 'Fraunces', serif; font-family: "Fraunces", serif;
font-style: italic; font-style: italic;
font-size: 0.85rem; font-size: 0.85rem;
color: var(--mauve); color: var(--mauve);
@@ -299,7 +328,7 @@ section.full-width {
gap: 0.5rem; gap: 0.5rem;
padding: 0.6rem 1.4rem; padding: 0.6rem 1.4rem;
border-radius: 4px; border-radius: 4px;
font-family: 'JetBrains Mono', monospace; font-family: "JetBrains Mono", monospace;
font-size: 0.78rem; font-size: 0.78rem;
letter-spacing: 0.05em; letter-spacing: 0.05em;
text-decoration: none; text-decoration: none;
@@ -311,7 +340,9 @@ section.full-width {
.btn-primary { .btn-primary {
background: var(--mauve); background: var(--mauve);
color: var(--crust); color: var(--crust);
box-shadow: 0 0 0 1px rgba(203, 166, 247, 0.25), 0 10px 26px rgba(17, 17, 27, 0.4); box-shadow:
0 0 0 1px rgba(203, 166, 247, 0.25),
0 10px 26px rgba(17, 17, 27, 0.4);
} }
.btn-primary:hover { .btn-primary:hover {
@@ -333,10 +364,11 @@ section.full-width {
/* Decorative grid bg */ /* Decorative grid bg */
#hero::before { #hero::before {
content: ''; content: "";
position: absolute; position: absolute;
inset: 0; inset: 0;
background-image: linear-gradient(var(--surface0) 1px, transparent 1px), background-image:
linear-gradient(var(--surface0) 1px, transparent 1px),
linear-gradient(90deg, var(--surface0) 1px, transparent 1px); linear-gradient(90deg, var(--surface0) 1px, transparent 1px);
background-size: 60px 60px; background-size: 60px 60px;
opacity: 0.18; opacity: 0.18;
@@ -377,7 +409,11 @@ section.full-width {
} }
.about-grid .about-block:first-child { .about-grid .about-block:first-child {
background: linear-gradient(90deg, rgba(203, 166, 247, 0.07), rgba(203, 166, 247, 0.03)); background: linear-gradient(
90deg,
rgba(203, 166, 247, 0.07),
rgba(203, 166, 247, 0.03)
);
border-left: 2px solid rgba(203, 166, 247, 0.65); border-left: 2px solid rgba(203, 166, 247, 0.65);
box-shadow: inset 0 0 0 1px rgba(203, 166, 247, 0.08); box-shadow: inset 0 0 0 1px rgba(203, 166, 247, 0.08);
} }
@@ -423,7 +459,7 @@ section.full-width {
} }
.version-timeline::before { .version-timeline::before {
content: ''; content: "";
position: absolute; position: absolute;
left: 0.65rem; left: 0.65rem;
top: 0.5rem; top: 0.5rem;
@@ -447,7 +483,9 @@ section.full-width {
border: 2px solid var(--surface1); border: 2px solid var(--surface1);
flex-shrink: 0; flex-shrink: 0;
margin-top: 0.15rem; margin-top: 0.15rem;
transition: border-color 0.2s, background 0.2s; transition:
border-color 0.2s,
background 0.2s;
position: relative; position: relative;
z-index: 1; z-index: 1;
} }
@@ -496,7 +534,9 @@ section.full-width {
font-size: 0.7rem; font-size: 0.7rem;
color: var(--overlay1); color: var(--overlay1);
margin-left: 0.3rem; margin-left: 0.3rem;
transition: color 0.2s, transform 0.2s; transition:
color 0.2s,
transform 0.2s;
display: inline-block; display: inline-block;
} }
@@ -525,7 +565,7 @@ section.full-width {
/* ─── Skills ───────────────────────────────────────────── */ /* ─── Skills ───────────────────────────────────────────── */
#skills h2 { #skills h2 {
font-family: 'Fraunces', serif; font-family: "Fraunces", serif;
font-size: 2.8rem; font-size: 2.8rem;
font-weight: 600; font-weight: 600;
color: var(--text); color: var(--text);
@@ -641,7 +681,7 @@ section.full-width {
/* ─── Tutorials / Projects shared ─────────────────────── */ /* ─── Tutorials / Projects shared ─────────────────────── */
.section-heading { .section-heading {
font-family: 'Fraunces', serif; font-family: "Fraunces", serif;
font-size: 2.8rem; font-size: 2.8rem;
font-weight: 600; font-weight: 600;
color: var(--text); color: var(--text);
@@ -707,7 +747,7 @@ section.full-width {
font-size: 0.68rem; font-size: 0.68rem;
letter-spacing: 0.04em; letter-spacing: 0.04em;
text-transform: uppercase; text-transform: uppercase;
font-family: 'JetBrains Mono', monospace; font-family: "JetBrains Mono", monospace;
cursor: pointer; cursor: pointer;
transition: all 0.2s; transition: all 0.2s;
} }
@@ -746,7 +786,7 @@ section.full-width {
font-size: 0.72rem; font-size: 0.72rem;
letter-spacing: 0.06em; letter-spacing: 0.06em;
text-transform: uppercase; text-transform: uppercase;
font-family: 'JetBrains Mono', monospace; font-family: "JetBrains Mono", monospace;
cursor: pointer; cursor: pointer;
transition: all 0.2s; transition: all 0.2s;
} }
@@ -790,7 +830,7 @@ section.full-width {
} }
.card::before { .card::before {
content: ''; content: "";
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
@@ -848,7 +888,9 @@ section.full-width {
.card-arrow { .card-arrow {
font-size: 0.8rem; font-size: 0.8rem;
color: var(--overlay0); color: var(--overlay0);
transition: color 0.2s, transform 0.2s; transition:
color 0.2s,
transform 0.2s;
align-self: flex-end; align-self: flex-end;
margin-top: 0.5rem; margin-top: 0.5rem;
} }
@@ -876,7 +918,7 @@ section.full-width {
} }
.contact-inner h2 { .contact-inner h2 {
font-family: 'Fraunces', serif; font-family: "Fraunces", serif;
font-size: 2.4rem; font-size: 2.4rem;
font-weight: 600; font-weight: 600;
color: var(--text); color: var(--text);
@@ -935,13 +977,13 @@ section.full-width {
} }
.contact-link-icon--github { .contact-link-icon--github {
-webkit-mask-image: url('icons/github.svg'); -webkit-mask-image: url("icons/github.svg");
mask-image: url('icons/github.svg'); mask-image: url("icons/github.svg");
} }
.contact-link-icon--linkedin { .contact-link-icon--linkedin {
-webkit-mask-image: url('icons/linkedin.svg'); -webkit-mask-image: url("icons/linkedin.svg");
mask-image: url('icons/linkedin.svg'); mask-image: url("icons/linkedin.svg");
} }
.contact-link-label { .contact-link-label {
@@ -988,7 +1030,9 @@ footer a:hover {
.reveal { .reveal {
opacity: 0; opacity: 0;
transform: translateY(24px); transform: translateY(24px);
transition: opacity 0.6s ease, transform 0.6s ease; transition:
opacity 0.6s ease,
transform 0.6s ease;
} }
.reveal.visible { .reveal.visible {

View File

@@ -1,4 +1,4 @@
<DOCTYPE! html> <!DOCTYPE html>
<html> <html>
<title>Convex Hull</title> <title>Convex Hull</title>

View File

@@ -1,43 +1,42 @@
h1{ h1 {
font-family: 'Roboto Condensed', sans-serif; font-family: "Roboto Condensed", sans-serif;
margin: 0; margin: 0;
padding: 0 0 15px 0; padding: 0 0 15px 0;
font-weight: 700; font-weight: 700;
font-size: 32px; font-size: 32px;
} }
h2{ h2 {
font-family: 'Roboto Condensed', sans-serif; font-family: "Roboto Condensed", sans-serif;
margin: 0; margin: 0;
padding: 15px 0 15px 0; padding: 15px 0 15px 0;
font-weight: 650; font-weight: 650;
font-size: 20px; font-size: 20px;
} }
p{ p {
margin: 1px; margin: 1px;
font-family: 'Roboto', sans-serif; font-family: "Roboto", sans-serif;
font-size: 19px; font-size: 19px;
padding: 5px 0px 5px 0px; padding: 5px 0px 5px 0px;
} }
.psudoCode{ .psudoCode {
font-family: 'Inconsolata', monospace; font-family: "Inconsolata", monospace;
font-size: 19px; font-size: 19px;
background-color: #D9D9D9; background-color: #d9d9d9;
border: 1px solid; border: 1px solid;
padding: 5px; padding: 5px;
box-shadow: 5px 5px #888888; box-shadow: 5px 5px #888888;
} }
a{ a {
font-family: 'Inconsolata', monospace; font-family: "Inconsolata", monospace;
font-size: 19px; font-size: 19px;
background-color: #D9D9D9; background-color: #d9d9d9;
} }
button{ button {
background-color: white; background-color: white;
border: 2px solid #555555; border: 2px solid #555555;
color: black; color: black;
@@ -50,7 +49,7 @@ button{
-webkit-transition-duration: 0.4s; /* Safari */ -webkit-transition-duration: 0.4s; /* Safari */
transition-duration: 0.4s; transition-duration: 0.4s;
cursor: pointer; cursor: pointer;
float:right; float: right;
} }
button:hover { button:hover {
@@ -58,12 +57,11 @@ button:hover {
color: white; color: white;
} }
.examples{ .examples {
padding: 10px; padding: 10px;
} }
html {
html{
-webkit-user-select: none; /* Safari */ -webkit-user-select: none; /* Safari */
-moz-user-select: none; /* Firefox */ -moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE10+/Edge */ -ms-user-select: none; /* IE10+/Edge */

View File

@@ -1,43 +1,42 @@
h1{ h1 {
font-family: 'Roboto Condensed', sans-serif; font-family: "Roboto Condensed", sans-serif;
margin: 0; margin: 0;
padding: 0 0 15px 0; padding: 0 0 15px 0;
font-weight: 700; font-weight: 700;
font-size: 32px; font-size: 32px;
} }
h2{ h2 {
font-family: 'Roboto Condensed', sans-serif; font-family: "Roboto Condensed", sans-serif;
margin: 0; margin: 0;
padding: 15px 0 15px 0; padding: 15px 0 15px 0;
font-weight: 650; font-weight: 650;
font-size: 20px; font-size: 20px;
} }
p{ p {
margin: 1px; margin: 1px;
font-family: 'Roboto', sans-serif; font-family: "Roboto", sans-serif;
font-size: 19px; font-size: 19px;
padding: 5px 0px 5px 0px; padding: 5px 0px 5px 0px;
} }
.psudoCode{ .psudoCode {
font-family: 'Inconsolata', monospace; font-family: "Inconsolata", monospace;
font-size: 19px; font-size: 19px;
background-color: #D9D9D9; background-color: #d9d9d9;
border: 1px solid; border: 1px solid;
padding: 5px; padding: 5px;
box-shadow: 5px 5px #888888; box-shadow: 5px 5px #888888;
} }
a{ a {
font-family: 'Inconsolata', monospace; font-family: "Inconsolata", monospace;
font-size: 19px; font-size: 19px;
background-color: #D9D9D9; background-color: #d9d9d9;
} }
button{ button {
background-color: white; background-color: white;
border: 2px solid #555555; border: 2px solid #555555;
color: black; color: black;
@@ -50,7 +49,7 @@ button{
-webkit-transition-duration: 0.4s; /* Safari */ -webkit-transition-duration: 0.4s; /* Safari */
transition-duration: 0.4s; transition-duration: 0.4s;
cursor: pointer; cursor: pointer;
float:right; float: right;
} }
button:hover { button:hover {
@@ -58,12 +57,11 @@ button:hover {
color: white; color: white;
} }
.examples{ .examples {
padding: 10px; padding: 10px;
} }
html {
html{
-webkit-user-select: none; /* Safari */ -webkit-user-select: none; /* Safari */
-moz-user-select: none; /* Firefox */ -moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE10+/Edge */ -ms-user-select: none; /* IE10+/Edge */

View File

@@ -1,38 +1,44 @@
<!DOCTYPE html> <!doctype html>
<html> <html>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js"></script>
<link rel="stylesheet" <link
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/atom-one-dark.min.css" /> rel="stylesheet"
<link rel="stylesheet" href="style.css" /> href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/atom-one-dark.min.css"
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.2.0/p5.min.js" />
<link rel="stylesheet" href="style.css" />
<script
src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.2.0/p5.min.js"
integrity="sha512-b/htz6gIyFi3dwSoZ0Uv3cuv3Ony7EeKkacgrcVg8CMzu90n777qveu0PBcbZUA7TzyENGtU+qZRuFAkfqgyoQ==" integrity="sha512-b/htz6gIyFi3dwSoZ0Uv3cuv3Ony7EeKkacgrcVg8CMzu90n777qveu0PBcbZUA7TzyENGtU+qZRuFAkfqgyoQ=="
crossorigin="anonymous"></script> crossorigin="anonymous"
<script src="script.js"></script> ></script>
<script> <script src="script.js"></script>
<script>
fetch("snippets/defineCount.js") fetch("snippets/defineCount.js")
.then(r => r.text()) .then((r) => r.text())
.then(code => { .then((code) => {
document.getElementById("define-count").textContent = code; document.getElementById("define-count").textContent = code;
if (window.hljs) hljs.highlightElement(document.getElementById("define-count")); if (window.hljs)
hljs.highlightElement(document.getElementById("define-count"));
}); });
fetch("snippets/countSquares.js") fetch("snippets/countSquares.js")
.then(r => r.text()) .then((r) => r.text())
.then(code => { .then((code) => {
document.getElementById("count-squares").textContent = code; document.getElementById("count-squares").textContent = code;
if (window.hljs) hljs.highlightElement(document.getElementById("count-squares")); if (window.hljs)
hljs.highlightElement(document.getElementById("count-squares"));
}); });
</script> </script>
<head> <head>
<h1>Summed-area Table</h1> <h1>Summed-area Table</h1>
<p> <p>
A Summed-area table is a data structure and algorithm for quickly and A Summed-area table is a data structure and algorithm for quickly and
efficiently generating the sum of values in a rectangular grid. In image efficiently generating the sum of values in a rectangular grid. In image
processing it is also known as an integral image. processing it is also known as an integral image.
</p> </p>
</head> </head>
<body> <body>
<p> <p>
To generate a summed-area table, each cell is given a value which is the To generate a summed-area table, each cell is given a value which is the
sum of all values in a rectangular subset where the top left corner is the sum of all values in a rectangular subset where the top left corner is the
@@ -64,8 +70,7 @@
</p> </p>
<p> <p>
Press <b>show rectangles</b> to see the different regions simplified and Press <b>show rectangles</b> to see the different regions simplified and
see how see how it changes the sum below.
it changes the sum below.
</p> </p>
<div id="sumDiv"> <div id="sumDiv">
<a id="subsetOutput">0</a> <a id="subsetOutput">0</a>
@@ -81,6 +86,5 @@
<button onclick="showCountToggle()">Show count</button> <button onclick="showCountToggle()">Show count</button>
<button onclick="toggleRectMode()">Show rectangles</button> <button onclick="toggleRectMode()">Show rectangles</button>
</body> </body>
</html> </html>

View File

@@ -1,43 +1,54 @@
@import url("https://fonts.googleapis.com/css2?family=Roboto&display=swap"); @import url("https://fonts.googleapis.com/css2?family=Roboto&display=swap");
html { html {
background-color: #2e3440; } background-color: #2e3440;
}
h1 { h1 {
color: #eceff4; color: #eceff4;
font-family: "Roboto", sans-serif; font-family: "Roboto", sans-serif;
font-size: 48pt; } font-size: 48pt;
}
p { p {
color: #d8dee9; color: #d8dee9;
font-family: "Roboto", sans-serif; font-family: "Roboto", sans-serif;
font-size: 16pt; } font-size: 16pt;
}
code { code {
font-size: 14pt; font-size: 14pt;
border: 4px solid #ebcb8b; } border: 4px solid #ebcb8b;
}
a { a {
color: #d8dee9; color: #d8dee9;
font-family: "Roboto", sans-serif; font-family: "Roboto", sans-serif;
font-size: 24pt; } font-size: 24pt;
}
#subsetOutput { #subsetOutput {
color: #88c0d0; } color: #88c0d0;
}
#wholeOutput { #wholeOutput {
color: #b48ead; } color: #b48ead;
}
#tlOutput { #tlOutput {
color: #d08770; } color: #d08770;
}
#tOutput { #tOutput {
color: #ebcb8b; } color: #ebcb8b;
}
#lOutput { #lOutput {
color: #a3be8c; } color: #a3be8c;
}
#sumDiv { #sumDiv {
text-align: center; } text-align: center;
}
button { button {
display: inline-block; display: inline-block;
@@ -47,22 +58,26 @@ button {
border: 4px solid #88c0d0; border: 4px solid #88c0d0;
box-sizing: border-box; box-sizing: border-box;
text-decoration: none; text-decoration: none;
font-family: 'Roboto',sans-serif; font-family: "Roboto", sans-serif;
font-size: 14pt; font-size: 14pt;
color: #434c5e; color: #434c5e;
background-color: #eceff4; background-color: #eceff4;
text-align: center; text-align: center;
position: relative; position: relative;
transition: all 0.15s; } transition: all 0.15s;
button:hover { }
button:hover {
background-color: #4c566a; background-color: #4c566a;
color: #eceff4; color: #eceff4;
border-color: #5e81ac; } border-color: #5e81ac;
button:focus { }
button:focus {
border: 4px solid #88c0d0; border: 4px solid #88c0d0;
box-sizing: border-box; } box-sizing: border-box;
}
#chillin { #chillin {
display: none; } display: none;
}
/*# sourceMappingURL=style.css.map */ /*# sourceMappingURL=style.css.map */

View File

@@ -1,79 +1,116 @@
<!DOCTYPE html> <!doctype html>
<html> <html>
<link href="style.css" rel="stylesheet" type="text/css"> <link href="style.css" rel="stylesheet" type="text/css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.2.0/p5.min.js" <script
src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.2.0/p5.min.js"
integrity="sha512-b/htz6gIyFi3dwSoZ0Uv3cuv3Ony7EeKkacgrcVg8CMzu90n777qveu0PBcbZUA7TzyENGtU+qZRuFAkfqgyoQ==" integrity="sha512-b/htz6gIyFi3dwSoZ0Uv3cuv3Ony7EeKkacgrcVg8CMzu90n777qveu0PBcbZUA7TzyENGtU+qZRuFAkfqgyoQ=="
crossorigin="anonymous"></script> crossorigin="anonymous"
></script>
<title>Travelling Sales Person</title> <title>Travelling Sales Person</title>
<h1>Travelling Sales Person Problem</h1> <h1>Travelling Sales Person Problem</h1>
<button id="backButton" onclick="window.location.href='/#tutorials'">Back</button> <button id="backButton" onclick="window.location.href = '/#tutorials'">
Back
</button>
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8" />
<div class="pictureContainer"> <div class="pictureContainer">
<img src="https://optimization.mccormick.northwestern.edu/images/e/ea/48StatesTSP.png" alt="TSP Problem" <img
id="Conway"> src="https://optimization.mccormick.northwestern.edu/images/e/ea/48StatesTSP.png"
alt="TSP Problem"
id="Conway"
/>
</div> </div>
<p>The travelling salesman problem (TSP) asks the following question: "Given a list of cities and the distances <p>
between each pair of cities, what is the shortest possible route that visits each city and returns to the origin The travelling salesman problem (TSP) asks the following question: "Given
city?" </p> a list of cities and the distances between each pair of cities, what is
<p>The problem was first formulated in 1930 and is one of the most intensively studied problems in optimization. It the shortest possible route that visits each city and returns to the
is used as a benchmark for many optimization methods. Even though the problem is computationally difficult, a origin city?"
large number of heuristics and exact algorithms are known, so that some instances with tens of thousands of </p>
cities can be solved completely and even problems with millions of cities can be approximated within a small <p>
fraction of 1%.</p> The problem was first formulated in 1930 and is one of the most
<p>The TSP has several applications even in its purest formulation, such as planning, logistics, and the manufacture intensively studied problems in optimization. It is used as a benchmark
of microchips. Slightly modified, it appears as a sub-problem in many areas, such as DNA sequencing. In these for many optimization methods. Even though the problem is computationally
applications, the concept city represents, for example, customers, soldering points, or DNA fragments, and the difficult, a large number of heuristics and exact algorithms are known, so
concept distance represents travelling times or cost, or a similarity measure between DNA fragments. The TSP that some instances with tens of thousands of cities can be solved
also appears in astronomy, as astronomers observing many sources will want to minimize the time spent moving the completely and even problems with millions of cities can be approximated
telescope between the sources. In many applications, additional constraints such as limited resources or time within a small fraction of 1%.
windows may be imposed.</p> </p>
<p>
The TSP has several applications even in its purest formulation, such as
planning, logistics, and the manufacture of microchips. Slightly modified,
it appears as a sub-problem in many areas, such as DNA sequencing. In
these applications, the concept city represents, for example, customers,
soldering points, or DNA fragments, and the concept distance represents
travelling times or cost, or a similarity measure between DNA fragments.
The TSP also appears in astronomy, as astronomers observing many sources
will want to minimize the time spent moving the telescope between the
sources. In many applications, additional constraints such as limited
resources or time windows may be imposed.
</p>
<h2>These are some of the algorithms I used</h2> <h2>These are some of the algorithms I used</h2>
<p>Note the purple route is the best route it's found so far and the thin white lines are the routes it's trying <p>
real time.</p> Note the purple route is the best route it's found so far and the thin
</head> white lines are the routes it's trying real time.
</p>
</head>
<body> <body>
<div class="canvasBody"> <div class="canvasBody">
<h3>Random Sort</h3> <h3>Random Sort</h3>
<span id="c1"></span> <span id="c1"></span>
<p class="canvasText">This canvas sorts through random possiblities. Every frame the program chooses two random <p class="canvasText">
points (cities) and swaps them around. eg say the order was London, Paris, Madrid, the program would swap This canvas sorts through random possiblities. Every frame the program
London and Paris so that the new order is: Paris, London, Madrid. The program then compares the distance chooses two random points (cities) and swaps them around. eg say the
against the record distance to decide whether the new order is better than the old order. This search method order was London, Paris, Madrid, the program would swap London and Paris
is the most inefficient way, the worst case scenario is never ending, as the point swaping is random the so that the new order is: Paris, London, Madrid. The program then
program may never reach the optimum route</p><br> compares the distance against the record distance to decide whether the
new order is better than the old order. This search method is the most
inefficient way, the worst case scenario is never ending, as the point
swaping is random the program may never reach the optimum route
</p>
<br />
<h3>Lexicographic Order</h3> <h3>Lexicographic Order</h3>
<span id="c2"></span> <span id="c2"></span>
<p class="canvasText">This canvas sorts through all possible orders sequentially, so after n! (where n is the <p class="canvasText">
number of points) this algorithm is guaranteed to have found the quickest possible route. However it is This canvas sorts through all possible orders sequentially, so after n!
highly inefficient always taking n! frames to complete and as n increases, time taken increases (where n is the number of points) this algorithm is guaranteed to have
exponentially.</p> found the quickest possible route. However it is highly inefficient
<a target="_blank" always taking n! frames to complete and as n increases, time taken
href="https://www.quora.com/How-would-you-explain-an-algorithm-that-generates-permutations-using-lexicographic-ordering">Click increases exponentially.
here to learn more about the algorithm</a><br> </p>
<a
target="_blank"
href="https://www.quora.com/How-would-you-explain-an-algorithm-that-generates-permutations-using-lexicographic-ordering"
>Click here to learn more about the algorithm</a
><br />
<h3>Genetic Algorithm</h3> <h3>Genetic Algorithm</h3>
<span id="c3"></span> <span id="c3"></span>
<p class="canvasText">This canvas is the most efficient at finding the quickest route, it is a mixture of the <p class="canvasText">
two methods above. It starts off by creating a population of orders, a fitness is then generated for each This canvas is the most efficient at finding the quickest route, it is a
order in the population. This fitness decides how likely the order is to be picked and is based on the mixture of the two methods above. It starts off by creating a population
distance it takes (lower distance is better). When two orders are picked, the algorithm splices the two of orders, a fitness is then generated for each order in the population.
together at a random term, it's then mutated and compared against the record distance. This takes the least This fitness decides how likely the order is to be picked and is based
amount of time to find the shortest distance as the algorithm doesn't search through permuations that are on the distance it takes (lower distance is better). When two orders are
obviously longer due to the order.</p><br> picked, the algorithm splices the two together at a random term, it's
then mutated and compared against the record distance. This takes the
least amount of time to find the shortest distance as the algorithm
doesn't search through permuations that are obviously longer due to the
order.
</p>
<br />
</div> </div>
</body> </body>
<script src="sketch.js"></script> <script src="sketch.js"></script>
<footer>
<p>This page was inspired by The Coding Train</p><a
href="https://www.youtube.com/channel/UCvjgXvBlbQiydffZU7m1_aw">Check him out here</a>
</footer>
<footer>
<p>This page was inspired by The Coding Train</p>
<a href="https://www.youtube.com/channel/UCvjgXvBlbQiydffZU7m1_aw"
>Check him out here</a
>
</footer>
</html> </html>

View File

@@ -1,15 +1,18 @@
@import url('https://fonts.googleapis.com/css?family=Roboto+Condensed'); @import url("https://fonts.googleapis.com/css?family=Roboto+Condensed");
a{font-family: 'Roboto Condensed', sans-serif; font-size: 18pt;} a {
font-family: "Roboto Condensed", sans-serif;
font-size: 18pt;
}
h1 { h1 {
font-family: 'Roboto Condensed', sans-serif; font-family: "Roboto Condensed", sans-serif;
margin: 0; margin: 0;
padding: 0 0 15px 0; padding: 0 0 15px 0;
} }
h2 { h2 {
font-family: 'Roboto Condensed', sans-serif; font-family: "Roboto Condensed", sans-serif;
margin: 0; margin: 0;
padding: 0 0 15px 0; padding: 0 0 15px 0;
text-align: center; text-align: center;
@@ -17,89 +20,150 @@ h2 {
} }
@media (min-width: 350px) { @media (min-width: 350px) {
h1 {font-size: 3.25em;} h1 {
img{height: 40px;} font-size: 3.25em;
p{font-size: 10px;} }
h2{font-size: 17px;} img {
height: 40px;
}
p {
font-size: 10px;
}
h2 {
font-size: 17px;
}
} }
@media (min-width: 400px) { @media (min-width: 400px) {
h1 {font-size: 3.25em;} h1 {
img{height: 45px;} font-size: 3.25em;
p{font-size: 15px;} }
h2{font-size: 17px;} img {
height: 45px;
}
p {
font-size: 15px;
}
h2 {
font-size: 17px;
}
} }
@media (min-width: 440px) { @media (min-width: 440px) {
h1 {font-size: 3.5em;} h1 {
img {height: 100px;} font-size: 3.5em;
p{font-size: 16px;} }
h2{font-size: 18px;} img {
height: 100px;
}
p {
font-size: 16px;
}
h2 {
font-size: 18px;
}
} }
@media (min-width: 500px) { @media (min-width: 500px) {
h1 {font-size: 3.75em;} h1 {
img{height: 125px;} font-size: 3.75em;
p{font-size: 16px;} }
h2{font-size: 19px;} img {
height: 125px;
}
p {
font-size: 16px;
}
h2 {
font-size: 19px;
}
} }
@media (min-width: 630px) { @media (min-width: 630px) {
h1 {font-size: 5em;} h1 {
img{height: 150px;} font-size: 5em;
p{font-size: 20px;} }
h2{font-size: 24px;} img {
height: 150px;
}
p {
font-size: 20px;
}
h2 {
font-size: 24px;
}
} }
@media (min-width: 768px) { @media (min-width: 768px) {
h1 { h1 {
font-size: 5em; font-size: 5em;
padding-bottom: 30px; padding-bottom: 30px;
} }
img{height: 175px;} img {
p{font-size: 22px;} height: 175px;
h2{font-size: 26px;} }
p {
font-size: 22px;
}
h2 {
font-size: 26px;
}
} }
@media (min-width: 1200px) { @media (min-width: 1200px) {
h1 {font-size: 8em;} h1 {
img{height: 250px;} font-size: 8em;
p{font-size: 24px;} }
h2{font-size: 28px;} img {
height: 250px;
}
p {
font-size: 24px;
}
h2 {
font-size: 28px;
}
} }
p{ p {
font-family: "Roboto Condensed", sans-serif;
font-family: 'Roboto Condensed', sans-serif;
} }
h3{ h3 {
text-align: center; text-align: center;
font-size: 30px; font-size: 30px;
font-family: 'Roboto Condensed', sans-serif; font-family: "Roboto Condensed", sans-serif;
} }
footer{ footer {
padding: 20px; padding: 20px;
background-color: #e0e0e0; background-color: #e0e0e0;
font-family: 'Roboto Condensed', sans-serif; font-family: "Roboto Condensed", sans-serif;
} }
@keyframes dimImg{ @keyframes dimImg {
from {opacity: 1; from {
filter: alpha(opacity=100);} opacity: 1;
to {opacity: 0.4; filter: alpha(opacity=100);
filter: alpha(opacity=50);} }
to {
opacity: 0.4;
filter: alpha(opacity=50);
}
} }
@keyframes revealText{ @keyframes revealText {
from {opacity: 0.4; from {
filter: alpha(opacity=50);} opacity: 0.4;
to {opacity: 1; filter: alpha(opacity=50);
filter: alpha(opacity=100);} }
to {
opacity: 1;
filter: alpha(opacity=100);
}
} }
.pictureContainer{ .pictureContainer {
float: right; float: right;
position: relative; position: relative;
} }
.pictureContainer a{ .pictureContainer a {
opacity: 0; opacity: 0;
position: absolute; position: absolute;
text-align: center; text-align: center;
@@ -107,36 +171,36 @@ footer{
left: 5px; left: 5px;
} }
.pictureContainer:hover img {
.pictureContainer:hover img{
animation-name: dimImg; animation-name: dimImg;
animation-duration: 1s; animation-duration: 1s;
opacity: 0.4; opacity: 0.4;
filter: alpha(opacity=50); filter: alpha(opacity=50);
} }
.pictureContainer:hover a{ .pictureContainer:hover a {
animation-name: revealText; animation-name: revealText;
animation-duration: 1s; animation-duration: 1s;
opacity: 1; opacity: 1;
} }
.canvasText{ .canvasText {
margin: 0px; margin: 0px;
display: inline-block; display: inline-block;
text-align: left; text-align: left;
} }
#c1, #c2, #c3{ #c1,
#c2,
#c3 {
display: block; display: block;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
width: 50%; width: 50%;
} }
.button{ .button {
padding: 16px 32px; padding: 16px 32px;
text-align: center; text-align: center;
text-decoration: none; text-decoration: none;
@@ -156,5 +220,3 @@ footer{
background-color: #555555; background-color: #555555;
color: white; color: white;
} }