Compare commits

...

38 Commits

Author SHA1 Message Date
d9045f23c2 added blog post about changing domains 2026-03-26 20:51:13 +01:00
816f9cefec replaced links to this website with natconf.dev 2026-03-26 20:21:39 +01:00
94416f5779 edited about page 2026-03-26 11:11:55 +01:00
7ab4a9a173 edited the button corner on the main page 2026-03-25 22:33:04 +01:00
2d052acf91 added warning banner to outdated postgresql blog post 2026-03-25 22:29:26 +01:00
5dee4fccc1 added new blog post 2026-03-25 22:18:57 +01:00
d0d74becc5 added subtitle to main page 2026-03-25 21:44:49 +01:00
0911a8892f added update about blog post 2026-03-17 17:10:56 +01:00
c95fd2a101 edited styling of code blocks to improve legibility 2026-03-17 17:06:59 +01:00
e118a71f76 added blog post 0317 2026-03-17 17:03:47 +01:00
3c47cbe581 added 88x31 buttons to main page 2026-03-14 22:22:39 +01:00
8c00b77d4e slight text edits to blog post 2026-03-14 17:54:25 +01:00
070e50e718 updated page title on Homesick page where it previously announced the page as Project N5 2026-03-14 10:45:47 +01:00
757510744f updated links to copyparty public share 2026-03-14 09:32:36 +01:00
c63aeabb6c added that homesick was previously called project n5 to the project page. should have done this before 2026-03-11 19:48:06 +01:00
4ff75057da renamed project n5 to homesick on main page 2026-03-11 19:33:44 +01:00
5558e3cf3b added update to link to drawing page 2026-03-11 19:22:34 +01:00
a186a0e4bc changed Project N5 references in some places to Homesick 2026-03-11 18:57:19 +01:00
e094d68dfa removed project n5 link from header; added homesick and drawing page links to footer 2026-03-11 18:54:33 +01:00
72b9d15381 adjusted gallery to scale down on mobile devices 2026-03-11 18:38:32 +01:00
013c6e38c7 added drawing to drawing gallery; added alt texts to all drawings 2026-03-10 23:17:24 +01:00
4683233272 removed redundant png screenshot from devlog files 2026-03-09 22:07:13 +01:00
b88908d7bf added devlog 2026/0309 2026-03-09 22:06:49 +01:00
a9756725da added link to art page to main page; added appropriate banner to art page; changed banner of project n5 page on main page to match the actual page banner 2026-03-09 17:40:30 +01:00
71010be02c added art page link to header and footer; removed unrelevant banner from art page 2026-03-09 17:21:54 +01:00
1f67da8996 added art page that links to its subpages 2026-03-09 17:17:07 +01:00
e1d8b1b5a4 made drawing gallery mobile-friendlier 2026-03-09 16:47:06 +01:00
7ecc34b762 finished adding content to drawing gallery for now 2026-03-09 16:43:26 +01:00
f2511470c5 moved discography page to art folder and added blurb to drawing gallery page 2026-03-09 16:13:08 +01:00
72a7515120 added drawing gallery page, not linked anywhere yet 2026-03-09 16:06:56 +01:00
6b42760737 removed redundant reverseTextOrder property from gallery 2026-03-07 20:37:37 +01:00
103b153dc0 show overflow on gallery entries to prevent text cutoff on hover 2026-03-07 15:01:57 +01:00
7263d5c136 removed uses, test pages 2026-03-07 14:18:53 +01:00
01f3f52958 removed portsmouth postmortem blog stub 2026-03-07 14:15:54 +01:00
bc9af8225a added website update 2026-03-07 14:14:01 +01:00
59ecefbe1a removed redundant no ai webring images; using local webring image instead of remote 2026-03-07 14:08:43 +01:00
656b51c58c widened banners on projects page 2026-03-07 14:05:39 +01:00
4aa078a9a2 changed width scaling of table of contents to better adapt to screen width 2026-03-07 13:47:28 +01:00
67 changed files with 1014 additions and 519 deletions

View File

@@ -1,5 +1,5 @@
# pages-svelte # pages-svelte
SvelteKit repository for my website hosted at [https://denizk0461.dev/](https://denizk0461.dev/) SvelteKit repository for my website hosted at [https://natconf.dev/](https://natconf.dev/)
![Screenshot of the website's main page](website-screenshot.webp) ![Screenshot of the website's main page](website-screenshot.webp)

View File

@@ -0,0 +1,32 @@
<script lang="ts">
import { type IndieButton } from "./indie-button";
let {
button
}: {
button: IndieButton;
} = $props();
</script>
<a class="button" href="{button.link}">
<img src="/common/buttons/{button.img}" alt="{button.alt}">
</a>
<style>
.button {
width: 88px;
height: 31px;
transition: scale var(--duration-animation) var(--anim-curve);
margin: 0;
}
.button:hover {
scale: 1.2;
z-index: 10;
}
.button img {
height: 100%;
width: 100%;
}
</style>

View File

@@ -0,0 +1,26 @@
export interface IndieButton {
img: string;
alt: string;
link?: string;
}
export let buttons: IndieButton[] = [
{
img: "iso8601.png",
alt: "A button with the text 'ISO 8601 YYYY-MM-DD'.",
link: "https://www.iso8601.com/",
},
{
img: "queercoded.png",
alt: "A button with the text \"you're telling me a queer coded this\" on a rainbow background.",
},
{
img: "madewithsveltekit.gif",
alt: "A red button with the text 'MADE WITH SVELTEKIT'.",
link: "https://svelte.dev",
},
{
img: "trans-rights-now.png",
alt: "A button with the text 'TRANS RIGHTS NOW!' next to a trans flag.",
},
];

View File

@@ -9,6 +9,7 @@
alignment, alignment,
video, video,
pixelated, pixelated,
smaller,
}: { }: {
image: string; image: string;
altText?: string; altText?: string;
@@ -16,6 +17,7 @@
alignment?: string; alignment?: string;
video?: boolean; video?: boolean;
pixelated?: boolean; pixelated?: boolean;
smaller?: boolean;
} = $props(); } = $props();
</script> </script>
@@ -39,18 +41,37 @@
{/if} {/if}
{/snippet} {/snippet}
<!-- this structure is ugly as fuck. there must be a better way of doing this -->
{#if alignment && alignment == "left"} {#if alignment && alignment == "left"}
<a class="subtitled-img-container subtitled-img-container-left" href="{image}"> {#if smaller}
{@render subtitledImageContent()} <a class="subtitled-img-container subtitled-img-container-left subtitled-img-reduced-size" href="{image}">
</a> {@render subtitledImageContent()}
</a>
{:else}
<a class="subtitled-img-container subtitled-img-container-left" href="{image}">
{@render subtitledImageContent()}
</a>
{/if}
{:else if alignment && alignment == "right"} {:else if alignment && alignment == "right"}
<a class="subtitled-img-container subtitled-img-container-right" href="{image}"> {#if smaller}
{@render subtitledImageContent()} <a class="subtitled-img-container subtitled-img-container-right subtitled-img-reduced-size" href="{image}">
</a> {@render subtitledImageContent()}
</a>
{:else}
<a class="subtitled-img-container subtitled-img-container-right" href="{image}">
{@render subtitledImageContent()}
</a>
{/if}
{:else} {:else}
<a class="subtitled-img-container subtitled-img-container-centred" href="{image}"> {#if smaller}
{@render subtitledImageContent()} <a class="subtitled-img-container subtitled-img-container-centred subtitled-img-reduced-size" href="{image}">
</a> {@render subtitledImageContent()}
</a>
{:else}
<a class="subtitled-img-container subtitled-img-container-centred" href="{image}">
{@render subtitledImageContent()}
</a>
{/if}
{/if} {/if}
<style> <style>
@@ -66,6 +87,7 @@
.subtitled-img-container-centred { .subtitled-img-container-centred {
width: var(--media-width); width: var(--media-width);
/* width: fit-content; */
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
} }
@@ -112,6 +134,10 @@
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
} }
.subtitled-img-reduced-size {
max-width: initial;
}
} }
@media screen and (min-width: 600px) { @media screen and (min-width: 600px) {
@@ -119,6 +145,10 @@
width: 34%; width: 34%;
} }
.subtitled-img-reduced-size {
max-width: 20%;
}
/* Don't limit height of images set to the side because text flows around them */ /* Don't limit height of images set to the side because text flows around them */
.subtitled-img-container-left img, .subtitled-img-container-right img { .subtitled-img-container-left img, .subtitled-img-container-right img {
max-height: initial; max-height: initial;

View File

@@ -80,10 +80,11 @@
} }
.toc-container { .toc-container {
width: 70%; max-width: var(--width-toc);
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
margin-top: 12px; margin-top: 12px;
box-sizing: border-box;
background-color: var(--color-background-highlight); background-color: var(--color-background-highlight);
padding: 16px 0; padding: 16px 0;
border: var(--border-style) var(--border-dash-size) var(--color-highlight); border: var(--border-style) var(--border-dash-size) var(--color-highlight);
@@ -134,11 +135,5 @@
.toc-level-1::before, .toc-level-2::before, .toc-level-3::before { .toc-level-1::before, .toc-level-2::before, .toc-level-3::before {
content: "└ "; content: "└ ";
} }
@media screen and (max-width: 550px) {
.toc-container {
width: 95%;
}
}
} }
</style> </style>

View File

@@ -22,7 +22,7 @@
</div> </div>
<span class="update-entry-timestamp-divider">::</span> <span class="update-entry-timestamp-divider">::</span>
<p> <p>
{entry.content} {@html entry.content}
{#if entry.link} {#if entry.link}
<a class="update-entry-link" href="{entry.link}">»</a> <a class="update-entry-link" href="{entry.link}">»</a>
{/if} {/if}

View File

@@ -10,21 +10,18 @@
let { let {
entries, entries,
reverseTextOrder,
}: { }: {
entries: GalleryEntry[]; entries: GalleryEntry[];
reverseTextOrder?: boolean;
} = $props(); } = $props();
</script> </script>
<div class="post-list"> <div class="post-list">
{#each entries as entry} {#each entries as entry}
{@render galleryEntry({entry, reverseTextOrder})} {@render galleryEntry({entry})}
{/each} {/each}
</div> </div>
{#snippet galleryEntry({entry, reverseTextOrder}: {entry: GalleryEntry, reverseTextOrder?: boolean})} {#snippet galleryEntry({entry}: {entry: GalleryEntry})}
<!-- {#snippet galleryEntry({key, post, index}: {key: string, post: DevlogPost, index: number})} -->
<a class="gallery-container" href="{entry.link}"> <a class="gallery-container" href="{entry.link}">
{#if entry.img && entry.img !== ""} {#if entry.img && entry.img !== ""}
<img class="gallery-img" src="{entry.img}" alt="{entry.imgAlt}"> <img class="gallery-img" src="{entry.img}" alt="{entry.imgAlt}">
@@ -32,15 +29,9 @@
<div class="gallery-img-placeholder"></div> <div class="gallery-img-placeholder"></div>
{/if} {/if}
<div class="gallery-text-container"> <div class="gallery-text-container">
{#if reverseTextOrder} <p class="gallery-subtitle">{@html entry.subtitle}</p>
<p class="gallery-subtitle">{@html entry.subtitle}</p> <p class="gallery-title">{entry.title}</p>
<p class="gallery-title">{entry.title}</p> <p class="gallery-description">{entry.description}</p>
<p class="gallery-description">{entry.description}</p>
{:else}
<p class="gallery-title">{entry.title}</p>
<p class="gallery-subtitle">{@html entry.subtitle}</p>
<p class="gallery-description">{entry.description}</p>
{/if}
</div> </div>
</a> </a>
{/snippet} {/snippet}
@@ -88,7 +79,6 @@
grid-auto-columns: 1fr; grid-auto-columns: 1fr;
grid-template-rows: 1fr 1fr 0fr; grid-template-rows: 1fr 1fr 0fr;
align-items: center; align-items: center;
overflow: hidden;
flex-grow: 1; flex-grow: 1;
padding: 0 16px; padding: 0 16px;
transition: background-color var(--duration-animation) var(--anim-curve), transition: background-color var(--duration-animation) var(--anim-curve),
@@ -105,8 +95,6 @@
.gallery-title { .gallery-title {
font-family: var(--font-mono); font-family: var(--font-mono);
font-weight: 700; font-weight: 700;
font-size: 1.4rem;
line-height: 2.0rem;
} }
.gallery-subtitle, .gallery-description { .gallery-subtitle, .gallery-description {
@@ -125,43 +113,43 @@
background-color: var(--color-background-highlight); background-color: var(--color-background-highlight);
backdrop-filter: blur(var(--blur-radius-background)); backdrop-filter: blur(var(--blur-radius-background));
} }
.gallery-container:hover .gallery-img, .gallery-container:hover .gallery-img-placeholder {
/* filter: grayscale(0%); */
/* margin-left: 8px; */
width: 260px;
}
.gallery-container:hover .gallery-text-container {
/* padding-right: 8px; */
grid-template-rows: 0fr 1fr 1fr;
}
.gallery-container:hover p { .gallery-container:hover p {
color: var(--color-highlight); color: var(--color-highlight);
} }
.gallery-container:hover .gallery-subtitle {
opacity: 0;
} @media screen and (min-width: 800px) {
.gallery-container:hover .gallery-description { .gallery-title {
opacity: 1; font-size: 1.4rem;
} line-height: 2.0rem;
@media screen and (max-width: 700px) {
/* .gallery-title {
font-size: 1.0rem;
line-height: 1.1rem;
} }
.gallery-subtitle {
font-size: 0.9rem;
line-height: 1.1rem;
} */
}
@media screen and (max-width: 500px) { .gallery-container:hover .gallery-img, .gallery-container:hover .gallery-img-placeholder {
width: 260px;
}
.gallery-container:hover .gallery-text-container {
grid-template-rows: 0fr 1fr 1fr;
}
.gallery-container:hover .gallery-subtitle {
opacity: 0;
}
.gallery-container:hover .gallery-description {
opacity: 1;
}
}
@media screen and (max-width: 800px) {
.gallery-title { .gallery-title {
font-size: 1.0rem; font-size: 1.0rem;
line-height: 1.1rem; line-height: 1.1rem;
} }
.gallery-description {
display: none;
}
.gallery-subtitle { .gallery-subtitle {
font-size: 0.8rem; font-size: 0.8rem;
line-height: 1rem; line-height: 1rem;
@@ -169,7 +157,7 @@
} }
.gallery-container { .gallery-container {
height: 64px; height: 72px;
} }
.gallery-text-container { .gallery-text-container {
@@ -177,8 +165,8 @@
} }
.gallery-img, .gallery-img-placeholder { .gallery-img, .gallery-img-placeholder {
width: 100px; width: 110px;
min-width: 100px; min-width: 110px;
} }
} }
</style> </style>

View File

@@ -20,19 +20,22 @@
<div class="content-container"> <div class="content-container">
<div class="content-box center-box"> <div class="content-box center-box">
<p> 20232026 denizk0461</p> <p> 20232026 denizk0461</p>
<p>Built from commit <a class="commit" href="https://code.denizk0461.dev/denizk0461/pages/src/commit/{version}">{version.substring(0, 6)}</a></p> <p>Built from commit <a class="commit" href="https://code.natconf.dev/denizk0461/pages/src/commit/{version}">{version.substring(0, 6)}</a></p>
</div> </div>
<div class="content-box"> <div class="content-box">
<h6>Content</h6> <h6>Content</h6>
<a href="/projects">Projects</a> <a href="/projects">Projects</a>
<a class="link-level-2" href="/projects/projectn5">Homesick</a>
<a href="/blog">Blog</a> <a href="/blog">Blog</a>
<a href="/art">Art</a>
<a class="link-level-2" href="/art/drawings">Drawings</a>
</div> </div>
<div class="content-box"> <div class="content-box">
<h6>Meta</h6> <h6>Meta</h6>
<a href="/meta/about">About</a> <a href="/meta/about">About</a>
<a href="/meta/feeds">Feeds</a> <a href="/meta/feeds">Feeds</a>
<a href="/meta/updates">Updates</a> <a href="/meta/updates">Updates</a>
<a href="https://code.denizk0461.dev/denizk0461/pages">Page Source</a> <a href="https://code.natconf.dev/denizk0461/pages">Page Source</a>
<a href="/meta/privacy">Privacy & Cookies</a> <a href="/meta/privacy">Privacy & Cookies</a>
</div> </div>
</div> </div>
@@ -66,6 +69,14 @@
margin: 0; margin: 0;
} }
.link-level-2 {
padding-left: 16px;
}
.link-level-2::before {
content: '└ ';
}
footer a:hover { footer a:hover {
font-weight: 700; font-weight: 700;
text-decoration: var(--border-style) var(--underline-dash-size) var(--color-highlight) underline; text-decoration: var(--border-style) var(--underline-dash-size) var(--color-highlight) underline;

View File

@@ -1,9 +1,8 @@
{#snippet headerLinks()} {#snippet headerLinks()}
<a href="/">Home</a> <a href="/">Home</a>
<a href="/projects">Projects</a> <a href="/projects">Projects</a>
<a href="/projects/projectn5">Project N5</a>
<a href="/blog">Blog</a> <a href="/blog">Blog</a>
<!-- <a href="/garden">Garden</a> --> <a href="/art">Art</a>
<a href="/meta/about">About</a> <a href="/meta/about">About</a>
{/snippet} {/snippet}

View File

@@ -97,12 +97,19 @@
font-style: normal; font-style: normal;
} }
@font-face {
font-family: "LIGHTYEARS";
src: url("/fonts/lightyears.woff2");
font-weight: 400;
font-style: normal;
}
/* OpenMoji */ /* OpenMoji */
@font-face { @font-face {
font-family: "OpenMoji"; font-family: "OpenMoji";
src: url("/fonts/openmoji/openmoji.woff2") format("woff2"); src: url("/fonts/openmoji/openmoji.woff2") format("woff2");
unicode-range: U+23,U+2A,U+2D,U+30-39,U+A9,U+AE,U+200D,U+203C,U+2049,U+20E3,U+2117,U+2120,U+2122,U+2139,U+2194-2199,U+21A9,U+21AA,U+229C,U+231A,U+231B,U+2328,U+23CF,U+23E9-23F3,U+23F8-23FE,U+24C2,U+25A1,U+25AA-25AE,U+25B6,U+25C0,U+25C9,U+25D0,U+25D1,U+25E7-25EA,U+25ED,U+25EE,U+25FB-25FE,U+2600-2605,U+260E,U+2611,U+2614,U+2615,U+2618,U+261D,U+2620,U+2622,U+2623,U+2626,U+262A,U+262E,U+262F,U+2638-263A,U+2640,U+2642,U+2648-2653,U+265F,U+2660,U+2663,U+2665,U+2666,U+2668,U+267B,U+267E,U+267F,U+2691-2697,U+2699,U+269B,U+269C,U+26A0,U+26A1,U+26A7,U+26AA,U+26AB,U+26B0,U+26B1,U+26BD,U+26BE,U+26C4,U+26C5,U+26C8,U+26CE,U+26CF,U+26D1,U+26D3,U+26D4,U+26E9,U+26EA,U+26F0-26F5,U+26F7-26FA,U+26FD,U+2702,U+2705,U+2708-270D,U+270F,U+2712,U+2714,U+2716,U+271D,U+2721,U+2728,U+2733,U+2734,U+2744,U+2747,U+274C,U+274E,U+2753-2755,U+2757,U+2763,U+2764,U+2795-2797,U+27A1,U+27B0,U+27BF,U+2934,U+2935,U+2B05-2B07,U+2B0C,U+2B0D,U+2B1B,U+2B1C,U+2B1F-2B24,U+2B2E,U+2B2F,U+2B50,U+2B55,U+2B58,U+2B8F,U+2BBA-2BBC,U+2BC3,U+2BC4,U+2BEA,U+2BEB,U+3030,U+303D,U+3297,U+3299,U+E000-E009,U+E010,U+E011,U+E040-E06D,U+E080-E0B4,U+E0C0-E0CC,U+E0FF-E10D,U+E140-E14A,U+E150-E157,U+E181-E189,U+E1C0-E1C4,U+E1C6-E1D9,U+E200-E216,U+E240-E269,U+E280-E283,U+E2C0-E2C4,U+E2C6-E2DA,U+E300-E303,U+E305-E30F,U+E312-E316,U+E318-E322,U+E324-E329,U+E32B,U+E340-E348,U+E380,U+E381,U+F000,U+F77A,U+F8FF,U+FE0F,U+1F004,U+1F0CF,U+1F10D-1F10F,U+1F12F,U+1F16D-1F171,U+1F17E,U+1F17F,U+1F18E,U+1F191-1F19A,U+1F1E6-1F1FF,U+1F201,U+1F202,U+1F21A,U+1F22F,U+1F232-1F23A,U+1F250,U+1F251,U+1F260-1F265,U+1F300-1F321,U+1F324-1F393,U+1F396,U+1F397,U+1F399-1F39B,U+1F39E-1F3F0,U+1F3F3-1F3F5,U+1F3F7-1F4FD,U+1F4FF-1F53D,U+1F549-1F54E,U+1F550-1F567,U+1F56F,U+1F570,U+1F573-1F57A,U+1F587,U+1F58A-1F58D,U+1F590,U+1F595,U+1F596,U+1F5A4,U+1F5A5,U+1F5A8,U+1F5B1,U+1F5B2,U+1F5BC,U+1F5C2-1F5C4,U+1F5D1-1F5D3,U+1F5DC-1F5DE,U+1F5E1,U+1F5E3,U+1F5E8,U+1F5EF,U+1F5F3,U+1F5FA-1F64F,U+1F680-1F6C5,U+1F6CB-1F6D2,U+1F6D5-1F6D7,U+1F6DC-1F6E5,U+1F6E9,U+1F6EB,U+1F6EC,U+1F6F0,U+1F6F3-1F6FC,U+1F7E0-1F7EB,U+1F7F0,U+1F90C-1F93A,U+1F93C-1F945,U+1F947-1F9FF,U+1FA70-1FA7C,U+1FA80-1FA89,U+1FA8F-1FAC6,U+1FACE-1FADC,U+1FADF-1FAE9,U+1FAF0-1FAF8,U+1FBC5-1FBC9,U+E0061-E0067,U+E0069,U+E006C-E0079,U+E007F; unicode-range: U+23,U+2A,U+2D,U+30-39,U+A9,U+AE,U+200D,U+203C,U+2049,U+20E3,U+2117,U+2120,U+2122,U+2139,U+2194-2199,U+21A9,U+21AA,U+229C,U+231A,U+231B,U+2328,U+23CF,U+23E9-23F3,U+23F8-23FE,U+24C2,U+25A1,U+25AA-25AE,U+25B6,U+25C0,U+25C9,U+25D0,U+25D1,U+25E7-25EA,U+25ED,U+25EE,U+25FB-25FE,U+2600-2605,U+260E,U+2611,U+2614,U+2615,U+2618,U+261D,U+2620,U+2622,U+2623,U+2626,U+262A,U+262E,U+262F,U+2638-263A,U+2640,U+2642,U+2648-2653,U+265F,U+2660,U+2663,U+2665,U+2666,U+2668,U+267B,U+267E,U+267F,U+2691-2697,U+2699,U+269B,U+269C,U+26A0,U+26A1,U+26A7,U+26AA,U+26AB,U+26B0,U+26B1,U+26BD,U+26BE,U+26C4,U+26C5,U+26C8,U+26CE,U+26CF,U+26D1,U+26D3,U+26D4,U+26E9,U+26EA,U+26F0-26F5,U+26F7-26FA,U+26FD,U+2702,U+2705,U+2708-270D,U+270F,U+2712,U+2714,U+2716,U+271D,U+2721,U+2728,U+2733,U+2734,U+2744,U+2747,U+274C,U+274E,U+2753-2755,U+2757,U+2763,U+2764,U+2795-2797,U+27A1,U+27B0,U+27BF,U+2934,U+2935,U+2B05-2B07,U+2B0C,U+2B0D,U+2B1B,U+2B1C,U+2B1F-2B24,U+2B2E,U+2B2F,U+2B50,U+2B55,U+2B58,U+2B8F,U+2BBA-2BBC,U+2BC3,U+2BC4,U+2BEA,U+2BEB,U+3030,U+303D,U+3297,U+3299,U+E000-E009,U+E010,U+E011,U+E040-E06D,U+E080-E0B4,U+E0C0-E0CC,U+E0FF-E10D,U+E140-E14A,U+E150-E157,U+E181-E189,U+E1C0-E1C4,U+E1C6-E1D9,U+E200-E216,U+E240-E269,U+E280-E283,U+E2C0-E2C4,U+E2C6-E2DA,U+E300-E303,U+E305-E30F,U+E312-E316,U+E318-E322,U+E324-E329,U+E32B,U+E340-E348,U+E380,U+E381,U+F000,U+F77A,U+F8FF,U+FE0F,U+1F004,U+1F0CF,U+1F10D-1F10F,U+1F12F,U+1F16D-1F171,U+1F17E,U+1F17F,U+1F18E,U+1F191-1F19A,U+1F1E6-1F1FF,U+1F201,U+1F202,U+1F21A,U+1F22F,U+1F232-1F23A,U+1F250,U+1F251,U+1F260-1F265,U+1F300-1F321,U+1F324-1F393,U+1F396,U+1F397,U+1F399-1F39B,U+1F39E-1F3F0,U+1F3F3-1F3F5,U+1F3F7-1F4FD,U+1F4FF-1F53D,U+1F549-1F54E,U+1F550-1F567,U+1F56F,U+1F570,U+1F573-1F57A,U+1F587,U+1F58A-1F58D,U+1F590,U+1F595,U+1F596,U+1F5A4,U+1F5A5,U+1F5A8,U+1F5B1,U+1F5B2,U+1F5BC,U+1F5C2-1F5C4,U+1F5D1-1F5D3,U+1F5DC-1F5DE,U+1F5E1,U+1F5E3,U+1F5E8,U+1F5EF,U+1F5F3,U+1F5FA-1F64F,U+1F680-1F6C5,U+1F6CB-1F6D2,U+1F6D5-1F6D7,U+1F6DC-1F6E5,U+1F6E9,U+1F6EB,U+1F6EC,U+1F6F0,U+1F6F3-1F6FC,U+1F7E0-1F7EB,U+1F7F0,U+1F90C-1F93A,U+1F93C-1F945,U+1F947-1F9FF,U+1FA70-1FA7C,U+1FA80-1FA89,U+1FA8F-1FAC6,U+1FACE-1FADC,U+1FADF-1FAE9,U+1FAF0-1FAF8,U+1FBC5-1FBC9,U+E0061-E0067,U+E0069,U+E006C-E0079,U+E007F;
} }
/* #endregion */ /* #endregion */
@@ -120,7 +127,8 @@
--color-header-highlight: color-mix(in srgb, #6d1e26 80%, transparent); --color-header-highlight: color-mix(in srgb, #6d1e26 80%, transparent);
--color-background: #111111; --color-background: #111111;
--color-background-highlight: color-mix(in srgb, var(--color-highlight) 10%, transparent); --color-background-highlight: color-mix(in srgb, var(--color-highlight) 20%, transparent);
--color-background-highlight-alt: color-mix(in srgb, var(--color-highlight-alt) 20%, transparent);
--color-background-highlight-hover: color-mix(in srgb, var(--color-highlight) 60%, transparent); --color-background-highlight-hover: color-mix(in srgb, var(--color-highlight) 60%, transparent);
--color-background-highlight-hover-dark: color-mix(in srgb, var(--color-highlight-dark) 60%, transparent); --color-background-highlight-hover-dark: color-mix(in srgb, var(--color-highlight-dark) 60%, transparent);
@@ -154,6 +162,8 @@
--font-mono: 'Kode Mono', 'OpenMoji', monospace; --font-mono: 'Kode Mono', 'OpenMoji', monospace;
--font-size-mono: 0.9em; --font-size-mono: 0.9em;
--font-lightyears: 'LIGHTYEARS', sans-serif;
--font-size-h1: 2.0rem; --font-size-h1: 2.0rem;
--font-size-h2: 1.5rem; --font-size-h2: 1.5rem;
--font-size-h3: 1.3rem; --font-size-h3: 1.3rem;
@@ -164,6 +174,7 @@
/* sizing */ /* sizing */
--media-width: 80%; --media-width: 80%;
--width-toc: 650px;
/* page sizing */ /* page sizing */
--page-width: 1200px; --page-width: 1200px;
@@ -234,9 +245,16 @@
color: var(--color-highlight); color: var(--color-highlight);
margin-top: var(--margin-header-top); margin-top: var(--margin-header-top);
margin-bottom: var(--margin-header-bottom); margin-bottom: var(--margin-header-bottom);
}
h1 {
width: fit-content; width: fit-content;
} }
h2, h3, h4, h5, h6 {
width: 100%;
}
h2 + h3, h3 + h4, h4 + h5, h5 + h6 { h2 + h3, h3 + h4, h4 + h5, h5 + h6 {
/* Remove top margin when a header immediately preceds another header */ /* Remove top margin when a header immediately preceds another header */
margin-top: 0; margin-top: 0;
@@ -298,6 +316,11 @@
padding-right: 6px; padding-right: 6px;
} }
.code-block {
width: fit-content;
padding: 16px;
}
img, video { img, video {
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
@@ -319,6 +342,10 @@
border: var(--border-dash-size) var(--border-style) var(--color-highlight); */ border: var(--border-dash-size) var(--border-style) var(--color-highlight); */
} }
.lightyears-text {
font-family: var(--font-lightyears);
}
.horizontally-centre-aligned { .horizontally-centre-aligned {
width: var(--media-width); width: var(--media-width);
display: flex; display: flex;
@@ -448,5 +475,15 @@
font-family: var(--font-sans-serif); font-family: var(--font-sans-serif);
padding: 4px; padding: 4px;
} }
.callout-warning {
margin: 12px auto;
max-width: var(--width-toc);
padding: 12px 20px;
box-sizing: border-box;
backdrop-filter: blur(var(--blur-radius-background));
background-color: var(--color-background-highlight-alt);
border: var(--border-dash-size) var(--border-style) var(--color-highlight-alt);
}
} }
</style> </style>

View File

@@ -1,12 +1,13 @@
<script lang="ts"> <script lang="ts">
import Content from "$lib/viewport/content.svelte"; import Content from "$lib/viewport/content.svelte";
import SubtitledImage from "$lib/components/subtitled-image.svelte";
import GalleryRow, { type GalleryRowEntry } from "$lib/lists/gallery-row.svelte"; import GalleryRow, { type GalleryRowEntry } from "$lib/lists/gallery-row.svelte";
import { posts as devlogPosts } from "./projects/projectn5/devlog/posts"; import { posts as devlogPosts } from "./projects/projectn5/devlog/posts";
import { posts as blogPosts } from "./blog/posts"; import { posts as blogPosts } from "./blog/posts";
import { entries as updateEntries } from "./meta/updates/updates"; import { entries as updateEntries } from "./meta/updates/updates";
import UpdateEntry from "$lib/components/update-entry.svelte"; import UpdateEntry from "$lib/components/update-entry.svelte";
import IndieButton from "$lib/components/indie-button.svelte";
import { buttons } from "$lib/components/indie-button";
let latestDevlogDate = devlogPosts[0].post.date; let latestDevlogDate = devlogPosts[0].post.date;
let latestBlogDate = blogPosts[0].post.date; let latestBlogDate = blogPosts[0].post.date;
@@ -15,10 +16,10 @@
const galleryTopRow: GalleryRowEntry[] = [ const galleryTopRow: GalleryRowEntry[] = [
{ {
title: "Project N5 devlog", title: "Homesick devlog",
description: `My active Godot game project about finding yourself in an unfamiliar future. <i>latest update: ${latestDevlogDate}</i>`, description: `My active Godot game project about finding yourself in an unfamiliar future. <i>latest update: ${latestDevlogDate}</i>`,
img: "projects/projectn5/devlog/2025/0523/birds_eye.webp", img: "projects/projectn5/banner2.webp",
altText: "Project N5 screenshot of Laura looking down at two cuboids.", altText: "The protagonist Laura standing on a floating platform in the purple test level. Ziplines are all around her and the text 'When this text is spinning, the game is not paused' is frozen in the sky.",
link: "projects/projectn5", link: "projects/projectn5",
}, },
{ {
@@ -33,24 +34,31 @@
const galleryBottomRow: GalleryRowEntry[] = [ const galleryBottomRow: GalleryRowEntry[] = [
{ {
title: "Projects", title: "Projects",
description: "An overview of what I do and have done", description: "An overview of my more technical projects",
img: "projects/banner.webp", img: "projects/banner.webp",
altText: "An upside-down New 3DS XL lying open on a desk with a small USB-C breakout board attached to it, and a USB-C cable plugged in. The 3DS is glowing to indicate that it is charging.", altText: "An upside-down New 3DS XL lying open on a desk with a small USB-C breakout board attached to it, and a USB-C cable plugged in. The 3DS is glowing to indicate that it is charging.",
link: "projects", link: "projects",
}, },
{
title: "Art",
description: "My creative side lives here",
img: "art/banner.webp",
altText: "A rainbow-like holographic effect produced by bending a reflective sheet of cardboard.",
link: "art",
},
{ {
title: "Files", title: "Files",
description: "Find things I've put for download on my Copyparty instance", description: "Find things I've put for download on my Copyparty instance",
img: "main/hypertext.webp", img: "main/hypertext.webp",
altText: "Screenshot of Hypertext Unity level. Crates are strewn across the floor, Waluigi is flying in front of the camera, and text such as 'COME AND TRY OUR ALL-NEW BLENDER' and 'omg! it's the brandenburg er tor!' is displayed.", altText: "Screenshot of Hypertext Unity level. Crates are strewn across the floor, Waluigi is flying in front of the camera, and text such as 'COME AND TRY OUR ALL-NEW BLENDER' and 'omg! it's the brandenburg er tor!' is displayed.",
link: "https://files.denizk0461.dev/", link: "https://files.natconf.dev/public/",
}, },
{ {
title: "Gitea", title: "Gitea",
description: "I now also self-host a Gitea instance where I am likely migrating all my projects to", description: "I now also self-host a Gitea instance where I am likely migrating all my projects to",
img: "main/magic.webp", img: "main/magic.webp",
altText: "A 'magic' command written in Java. The command shuts down the computer when ran.", altText: "A 'magic' command written in Java. The command shuts down the computer when ran.",
link: "https://code.denizk0461.dev/", link: "https://code.natconf.dev/",
}, },
]; ];
</script> </script>
@@ -62,10 +70,12 @@
<Content> <Content>
<h1 class="gradient-title"><i>Moin!</i> ~ welcome to my website :)</h1> <h1 class="gradient-title"><i>Moin!</i> ~ welcome to my website :)</h1>
<a href="/blog/2026/0325" class="page-subtitle gradient-title lightyears-text">you can change the world from your bedroom!</a>
<hr> <hr>
<div> <div>
<img class="me-img pixelated-img" src="me.webp" alt="Pixelated mirror selfie of the website creator wearing a green shirt, fitting the website theme. The face is obscured." title="hi!"> <img class="me-img pixelated-img" src="me.webp" alt="Pixelated mirror selfie of the website creator wearing a green shirt, fitting the website theme. The face is obscured." title="hi!">
<p>Hi! I'm Deniz. Welcome to my website! I keep rewriting this introduction but I'm REALLY bad at this type of stuff.</p> <p>Hi! I'm Deniz. Welcome to my website! I keep rewriting this introduction but I'm REALLY bad at this type of stuff.</p>
@@ -91,34 +101,45 @@
<GalleryRow entries={galleryTopRow} /> <GalleryRow entries={galleryTopRow} />
<GalleryRow entries={galleryBottomRow} /> <GalleryRow entries={galleryBottomRow} />
<hr> <div class="split-container">
<div class="webring-container">
<h4 class="update-header">webrings</h4>
<div class="webring">
<iframe title="bucket webring" id="bucket-webring" style="width: 100%; height: 3rem; border: none;" src="https://webring.bucketfish.me/embed.html?name=denizk0461"></iframe>
</div>
<div class="webring-container"> <div class="webring">
<div class="webring"> <a href="https://www.rainbowcemetery.com/devring/prev.php?id=18">
<iframe title="bucket webring" id="bucket-webring" style="width: 100%; height: 3rem; border: none;" src="https://webring.bucketfish.me/embed.html?name=denizk0461"></iframe> <img src="/webrings/gamedev/ringprev.png" alt="previous">
</a>
<a href="https://www.rainbowcemetery.com/devring/list.php?id=18">
<img src="/webrings/gamedev/88x31.png" alt="list">
</a>
<a href="https://www.rainbowcemetery.com/devring/next.php?id=18">
<img src="/webrings/gamedev/ringnext.png" alt="next">
</a>
</div>
<map name="w95widget">
<area href="https://baccyflap.com/noai" target="_blank" shape="rect" coords="0,0,308,22" alt="no ai webring" title="no ai webring">
<area href="https://baccyflap.com/noai/?prv&s=dzk" target="_top" shape="rect" coords="56,36,130,58" alt="previous" title="previous">
<area href="https://baccyflap.com/noai/?rnd" target="_top" shape="rect" coords="137,36,211,58" alt="random" title="random">
<area href="https://baccyflap.com/noai/?nxt&s=dzk" target="_top" shape="rect" coords="218,36,292,58" alt="next" title="next">
</map>
<img class="webring-img" usemap="#w95widget" src="/webrings/noai/w95widget.webp" alt="a gray Windows 95 style dialog box titled 'The No AI Webring' with a little icon showing a computer chip in a rubbish bin. beside it are three clickable buttons, labeled Previous, Random... and Next">
</div> </div>
<div class="webring"> <div class="button-container">
<a href="https://www.rainbowcemetery.com/devring/prev.php?id=18"> <h4 class="update-header">button corner</h4>
<img src="/webrings/gamedev/ringprev.png" alt="previous"> <div class="button-subcontainer">
</a> {#each buttons as button}
<a href="https://www.rainbowcemetery.com/devring/list.php?id=18"> <IndieButton button={button} />
<img src="/webrings/gamedev/88x31.png" alt="list"> {/each}
</a> </div>
<a href="https://www.rainbowcemetery.com/devring/next.php?id=18"> <p>to be expanded!</p>
<img src="/webrings/gamedev/ringnext.png" alt="next"> <p class="small-supertext">my own 88x31 button is in the making. ETA: ???</p>
</a>
</div> </div>
<map name="w95widget">
<area href="https://baccyflap.com/noai" target="_blank" shape="rect" coords="0,0,308,22" alt="no ai webring" title="no ai webring">
<area href="https://baccyflap.com/noai/?prv&s=dzk" target="_top" shape="rect" coords="56,36,130,58" alt="previous" title="previous">
<area href="https://baccyflap.com/noai/?rnd" target="_top" shape="rect" coords="137,36,211,58" alt="random" title="random">
<area href="https://baccyflap.com/noai/?nxt&s=dzk" target="_top" shape="rect" coords="218,36,292,58" alt="next" title="next">
</map>
<img class="webring-img" usemap="#w95widget" src="https://baccyflap.com/noai/w95widget.gif" alt="a gray Windows 95 style dialog box titled 'The No AI Webring' with a little icon showing a computer chip in a rubbish bin. beside it are three clickable buttons, labeled Previous, Random... and Next">
</div> </div>
</Content> </Content>
<style> <style>
@@ -127,7 +148,7 @@
display: flex; display: flex;
flex-direction: row; flex-direction: row;
gap: 16px; gap: 16px;
margin-bottom: 16px; margin: 16px 0;
} }
@media screen and (max-width: 900px) { @media screen and (max-width: 900px) {
@@ -137,6 +158,18 @@
} }
} }
.button-container {
flex: 2;
}
.button-subcontainer {
display: flex;
flex-wrap: wrap;
gap: 8px;
justify-content: center;
align-content: center;
}
.webring { .webring {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@@ -155,21 +188,23 @@
margin-right: 12px; margin-right: 12px;
} }
.update-container, .info-container { .update-container, .info-container, .button-container, .webring-container {
padding: 8px 24px; padding: 8px 24px;
backdrop-filter: blur(var(--blur-radius-background)); backdrop-filter: blur(var(--blur-radius-background));
flex: 2; flex: 2;
} }
.update-container { .update-container, .button-container, .webring-container {
border: var(--border-dash-size) var(--color-highlight) var(--border-style); border: var(--border-dash-size) var(--color-highlight) var(--border-style);
} }
.info-container { .info-container, .button-container {
border: var(--border-dash-size) var(--color-highlight-alt) var(--border-style); border: var(--border-dash-size) var(--color-highlight-alt) var(--border-style);
}
.info-container {
flex: 1; flex: 1;
} }
.info-container > h4 { .info-container > h4, .button-container > h4 {
color: var(--color-highlight-alt); color: var(--color-highlight-alt);
} }
.info-container p, .info-container a { .info-container p, .info-container a {
@@ -183,6 +218,7 @@
flex-wrap: wrap; flex-wrap: wrap;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
flex: 3;
/* flex-wrap: wrap; */ /* flex-wrap: wrap; */
/* gap: 8px; */ /* gap: 8px; */
padding-top: 16px; padding-top: 16px;
@@ -209,6 +245,12 @@
background-clip: text; background-clip: text;
-webkit-background-clip: text; -webkit-background-clip: text;
-webkit-text-fill-color: transparent; -webkit-text-fill-color: transparent;
padding-bottom: 12px; }
.page-subtitle {
/* padding-bottom: 12px; */
width: fit-content;
margin: 4px 0 12px 0;
display: block;
} }
</style> </style>

View File

@@ -0,0 +1,39 @@
<script lang="ts">
import Banner2 from "$lib/banner2.svelte";
import Content from "$lib/viewport/content.svelte";
import GalleryRow, { type GalleryRowEntry } from "$lib/lists/gallery-row.svelte";
const subpages: GalleryRowEntry[] = [
{
title: "Drawing Gallery",
description: "Some cool things I've drawn!",
img: "drawings/banner.webp",
altText: "Several Faber-Castell Polychromos colour pencils lined up with markings next to them in the same colour on a sheet of paper.",
link: "drawings",
},
{
title: "Discography",
description: "Small stories about my past music",
img: "/main/hypertext.webp",
altText: "",
link: "music",
},
];
</script>
<svelte:head>
<title>Art | denizk0461</title>
</svelte:head>
<Content>
<Banner2
title="Art"
banner="banner.webp"
bannerAlt="A rainbow-like holographic effect produced by bending a reflective sheet of cardboard."
subtitle="my creative side" />
<p>Here I have collected the products of some of my creative endeavours. Check them out below!</p>
<GalleryRow entries={subpages} />
</Content>

View File

@@ -0,0 +1,137 @@
<script lang="ts">
import Banner2 from "$lib/banner2.svelte";
import Content from "$lib/viewport/content.svelte";
import { type Drawing, drawings } from "./drawings";
</script>
<svelte:head>
<title>Drawing Gallery | denizk0461</title>
</svelte:head>
{#snippet drawingGalleryEntry({d}: {d: Drawing})}
<div class="gallery-entry">
<div class="gallery-entry-img-container">
<img src="{d.img}" alt="{d.imgAlt}">
</div>
<div class="gallery-entry-info">
<p class="gallery-entry-title">{d.title} <span>{d.date}</span></p>
{#each d.notes as note}
<p class="gallery-entry-note">{note}</p>
{/each}
<a href="{d.img}">view full-size</a>
</div>
</div>
{/snippet}
<Content>
<Banner2
title="Drawing Gallery"
subtitle=""
banner="banner.webp"
bannerAlt="Several Faber-Castell Polychromos colour pencils lined up with markings next to them in the same colour on a sheet of paper." />
<p>I started drawing at the start of 2026 and this is my page to show off what I make! I've mostly drawn on paper so far (I like the feel and resistance of pens on paper as well as the <a href="/blog/2026/0129">limitations</a> it imposes), but I got into digital art with Krita recently!</p>
<p>Why have I created this page, you may wonder? to pressure myself to draw more</p>
<p>If you're interested, here's a post about me <a href="/blog/2026/0205">drawing every day for 28 days</a> to learn to draw. You may recognise some of the drawings there; I picked out my favourite drawings and added them here!</p>
<div class="drawing-container">
{#each drawings as d}
{@render drawingGalleryEntry({d})}
{/each}
</div>
</Content>
<style>
.drawing-container {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 8px;
}
.gallery-entry {
position: relative;
height: 340px;
}
.gallery-entry-img-container {
overflow: hidden;
}
.gallery-entry-img-container, .gallery-entry-info {
border-radius: 16px;
}
.gallery-entry-img-container, .gallery-entry-info {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: 0;
height: 100%;
width: 100%;
}
.gallery-entry img {
width: 100%;
height: 100%;
object-fit: cover;
transition: scale var(--duration-animation) var(--anim-curve);
}
.gallery-entry:hover img {
scale: 1.2;
}
.gallery-entry:hover .gallery-entry-info {
opacity: 1;
}
.gallery-entry-info {
opacity: 0;
display: flex;
flex-direction: column;
transition: opacity var(--duration-animation) var(--anim-curve);
background-color: var(--color-header-highlight);
padding: 12px 8px;
gap: 4px;
box-sizing: border-box;
justify-content: center;
outline: var(--border-style) var(--border-dash-size) var(--color-highlight-alt);
}
.gallery-entry-info * {
margin: 0;
width: fit-content;
}
.gallery-entry-title {
font-family: var(--font-mono);
font-weight: 700;
}
.gallery-entry-title span {
font-size: 0.8rem;
line-height: 0.9rem;
font-weight: 500;
}
.gallery-entry-note, .gallery-entry-info a {
font-size: 1.0rem;
line-height: 1.3rem;
}
@media screen and (max-width: 1000px) {
.drawing-container {
grid-template-columns: 1fr 1fr;
}
}
@media screen and (max-width: 600px) {
.drawing-container {
grid-template-columns: 1fr;
}
}
</style>

View File

@@ -0,0 +1,90 @@
export interface Drawing {
title: string;
date: string;
notes: string[];
img: string;
imgAlt: string;
}
export let drawings: Drawing[] = [
{
title: "Krita #1",
date: "2026-03-10",
notes: [
"ok i changed my mind on digital art. it's awesome.",
"My first drawing using Krita! I went with my usual methods but tried refining some things and adding (hopefully not overly misplaced) shadows too. I ended up really liking the ability to use layers, and colour in digital art just pops so nicely.",
"Initially, I drew the left arm in front of her body but later changed this to avoid drawing the hand.",
],
img: "2026/0310.webp",
imgAlt: "A digital drawing of a girl with long brown hair in a ponytail. She has green eyes and is wearing a cropped shirt with stripes, an orange spaghetti top underneath, and dark trousers. She is holding her hands behind her back.",
},
{
title: "SMILE! :D",
date: "2026-03-04",
notes: [
"This is actually the construction sketch of a drawing I later went over with a fineliner and coloured pencils. However, I kind of prefer the pencil sketch.",
"This was my first attempt at a head-on perspective. I had fun drawing details like the scrunchie, the jeans, and the smile!",
],
img: "/blog/2026/0205/27-1.webp",
imgAlt: "A drawing of a girl with her head tilted towards her right shoulder. She is smiling with her eyes closed and is holding up a victory sign with her left hand. She has her hair in a ponytail and is wearing jeans with shoulder straps, and there is a scrunchie on her left wrist as well.",
},
{
title: "Cyborg Arm",
date: "2026-02-27",
notes: [
"Possibly my favourite sketch from the drawing challenge, because she looks cool, but also because her design deviates from the other characters a bit.",
],
img: "/blog/2026/0205/22.webp",
imgAlt: "A pencil sketch of a girl with a ponytail, crop top, and track pants with a slightly shocked look on her face. She is looking at her right arm, which is a cyborg part.",
},
{
title: "Porter Robinson fanart",
date: "2026-02-26",
notes: [
"I drew the Worlds hand for practice and then decided to draw Po-Uta's head as well. I realised then that learning to draw gave me the ability to draw fanart.",
"I had never considered that possibility before.",
],
img: "/blog/2026/0205/21.webp",
imgAlt: "Two pencil sketches traced over with a black fineliner. The left one is of a hand with a cube in its palm, sketched after the hand on the cover of Porter Robinson's album Worlds. Beneath it is an emoticon used on the same cover. To the right is a manga-style head with green eyes and wavy hair, meant to resemble Porter Robinson's Vocaloid mascot Po-Uta.",
},
{
title: "Emilia",
date: "2026-02-23",
notes: [
"My first character with the new style of drawing eyes I picked up from a manga drawing book!",
"I named her Emilia because she looked like a more nice and caring character.",
],
img: "/blog/2026/0205/18.webp",
imgAlt: "A pencil sketch of a girl holding up a V sign with her left arm. She is wearing a long-sleeve shirt, jeans, and her hair is tied up in a ponytail. She is winking, the other eye is coloured green. Her body is tilted towards the right side of the paper. In the top right corner is a lightly-drawn sketch of the girl's pose.",
},
{
title: "Elizabeth",
date: "2026-02-18",
notes: [
"She's the product of me trying to re-draw the character I drew on day 1 of my drawing challenge, and I was really glad to see that I had actually improved!",
],
img: "/blog/2026/0205/13-2.webp",
imgAlt: "A pencil drawing of a girl looking to the left. She is wearing a cropped loose tee and jeans, while her right hand is hinted to rest on her hip.",
},
{
title: "bread girl",
date: "2026-01-30",
notes: [
"I drew her during a game of Wizard. I initially wanted to make her chew on a whole loaf but I didn't know how to draw that.",
"Wasn't really sure how to convey that her mouth is full either, but in retrospect, I could have exaggerated the bow in her lower eyelids to do so.",
"I like her eyes. Her head could be taller, actually.",
],
img: "2026/breadgirl.webp",
imgAlt: "An anime-style girl chewing on a piece of bread. She wears a ponytail and a sleeveless top.",
},
{
title: "test",
date: "2026-01-29",
notes: [
"A small sketch (only like 4cm wide) that I drew with a ballpoint pen on pink paper. The fact that I was able to sketch this, without any prior practice, plus an intrinsic want to be able to draw made me seriously consider learning to draw.",
"Having learned just a little bit about drawing, I can say now (a month and a half later) that this isn't great, but it served its purpose of making me start to draw!",
],
img: "/blog/2026/0129/girl.webp",
imgAlt: "A small drawing of an anime-style girl's head. She has a ponytail and is looking towards the left with a concentrated gaze.",
},
];

View File

@@ -33,5 +33,5 @@
banner="robert.webp" banner="robert.webp"
bannerAlt="View at a tram bridge rising and then curving to the left." /> bannerAlt="View at a tram bridge rising and then curving to the left." />
<Gallery entries={entries} reverseTextOrder /> <Gallery entries={entries} />
</Content> </Content>

View File

@@ -1,123 +0,0 @@
<script lang="ts">
import ImageSubtitle from "$lib/image-subtitle.svelte";
</script>
Recently, I finished a nearly four-month abroad semester in Portsmouth, UK. I wanted to ramble on about my experiences in more detail. While I already have to write a report about my experience as part of the ERASMUS program which funded most of the trip, they aim for 'at least 3 pages' and my first draft exceeded that by nearly 200%...
![](clouds.webp)
![](gunwharf.webp)
## The Trip
My trip to Portsmouth went surprisingly well. German trains are usually known for their delays, but both the EC to Cologne as well as the ICE to Brussels were pretty much perfectly on time.
Taking the Eurostar was a weird experience. Having to go through security checks is strange, both because the UK *should* still be in the EU (it was such a strange and genuinely terrible decision to leave) and because I was boarding a *train*, not a plane. The checks were quite quick though, just a scan of the luggage, then a passport check, and then it's off to the waiting area. You're only allowed to board the train about 20 minutes before departure, but you must arrive to the security checks at least 30 minutes ahead 90 minutes are recommended, and it's honestly probably a good decision to come this early. The waiting area did not have *nearly* enough seats for everyone, but it was fine.
For my trip, I bought an Interrail ticket. They have tickets specifically for ERASMUS students that allow you 4 or 6 days of travel within 6 months instead of just one, but you're only allowed one outbound and one inbound trip to your home country. The ticket is great because it gives you complete freedom over which trains to use **except** the Eurostar, as you *must* book a seat in advance. Those cost 32€. The good thing is that you have a safe seat. It's important to book somewhat early (2 weeks prior is probably fine) and manually pick a seat, as the system can place you on a window-side seat with no view.
The Eurostar trip was quite pleasant. Less pleasant was the ride from London St. Pancras to London Waterloo, for which I took the Northern tube line. I needed to switch branches, so I had to take the tube the opposite way to Euston before switching over to another train headed towards Waterloo. Kind of annoying, but it didn't take overly long.
The last train of the day was the Southwestern to Portsmouth. The ride was fine, though crowded (it departs from London, after all), though I dislike how a lot of British trains have a 3+2 seat layout; within a single row, there are 3 seats on one side, and two more on the other. Since the trains aren't any bigger than they are in Germany, the seats have to be narrower, and the walkway between them is smaller as well. If you have someone sitting right next to you, both of you will be very cramped, even if you're slim. It's an all-around uncomfortable experience.
So, my train rides were fine, right. Everything went well. Except the very last part of my trip. After 12 hours of train travel, I was preparing to take the number 13 bus that runs to Langstone campus. This bus, however, departed mere minutes before I arrived. At least partially at fault were probably the ticket gates (that the UK likes to place at a lot of train stations (despite the fact that staff does check tickets on board)) as well as the bridge that was awkwardly fenced off and required a longer walk as the ticket gates were obviously retrofitted.
What did I do after missing the bus? I walked. As the 13 runs hourly, it didn't make sense to wait. Given how that walk went though, I kinda wish I had waited. For the entire 40-ish minute walk, during which I of course had to carry my heavy luggage, I had to bear the UK's terrible infrastructure. Sidewalks that were crooked and constantly pulled my rolling luggage towards the street as well as traffic lights that effectively penalised walking by delaying on-foot traffic made the trip quite miserable. No lie, I was already regretting the abroad semester by this point, and I hadn't even made it to my flat yet.
## Student Housing
I lived in Langstone West. The housing was decently nice, but the first impressions were quite bad.
I was first greeted by a rather dilapidated campus. Green patches were interrupted by uneven foundations that have seemingly been rotting away for years. An array of shipping containers blocked the view to the north end of the campus. Worst of all was the tall-standing building, the *Barnard Tower*, which looked as if it had been abandoned for a decade. I was mentally preparing for the tower to be my home for the next few months until someone let me know that the student halls were actually further north on the property.
![View at the Langstone campus site from the north end.](langstone.webp)
When I arrived, I met a security officer at the reception who handed me my keys and led me to my room. Weirdly, the literal second I turned my back on him as I tried pushing my luggage into my room, he left. This felt pretty unfriendly and was quite surprising considering the actual reception staff was nothing like this. They were genuinely so nice and understanding and forthcoming about everything you mentioned to them. They greeted you when you passed them, they were up for chitchat, they were just super friendly. One time, our microwave broke, and they literally handed us a new one on the spot. Never had issues with them.
The accommodation was the second-cheapest available (cheapest with individual bathrooms), so I was worried what would await me, but to my surprise, the flats were actually pretty nice. We had a big kitchen with two ovens, two fridges, two sinks, a big stovetop and a lounge area with a TV. The rooms were decent too pretty small (around 10m²) but you had everything you needed for the short stay. The walls were not very insulating, which was quite noticeable in winter when the nightly temperatures dropped near freezing and the cold crept into the rooms overnight. The electric heaters were good though; you could tune them by the degree and even program them so that they would turn on and off at specific times.
Something that I found odd during my stay and especially once I returned to my flat in Germany was how the room inventory seemed fit for smaller people. The chair was much smaller than my IKEA Markus (which, when I first sat in it after my abroad semester felt as if it hugged me); the desk is set very low and crossing my legs beneath it was near-impossible (often cut off my blood flow because of it); the mirror in the bathroom hung lower; the shower was smaller; even the ceiling hung lower than at home!
## The Environment
Living at Langstone was a mixed experience.
The most impactful negative aspect was that there were no supermarkets nearby. A Premier store was at the southern end of the campus, but it's a really tiny one, and Premier isn't exactly a place where you can go to do your shopping in my mind, it's just a shop for alcohol and snacks that for some reason also sells household items and vegetables. The next Co-Op was a 16-minute walk away; Lidl, Tesco, Aldi were almost half an hour away on foot.
I also think that there wasn't a lot to do at Langstone? There was nothing noteworthy nearby. Milton Common was the nearest thing to a park, and aside from that, all you had was residential area. Both the university as well as Commercial Road and Gunwharf Quays (meaning virtually all shops) were on the far end of the island.
However, a 20-minute walk brings you to Eastney beach a great spot for when you need a quiet spot outdoors that offers a pretty view at the Solent with the Isle of Wight on the other side. It's a rocky beach (pebbles instead of sand). In fact, all along Portsea Island's perimeter you had some very nice beaches; South Parade Pier was quite nice with the Rock and Rose Gardens nearby, and the waterfront area at Gunwharf Quays was quite pretty too. The beaches are currently being renewed; Portsmouth is reinforcing its sea defences, and while doing that, they're going a step further by redeveloping waterfront areas. It's quite nice and well worth the visits.
If you want to party and don't mind noise, you may want to look for another accommodation though, as Langstone itself was quite a serene place. It's a quiet place where you can really live a stress-free life. At least that's the experience I had, and I quite liked that.
## The City
![](portsdownhill_dark.webp)
![](portsdownhill.webp)
![](spinnakertower.webp)
## The Trouble With Buses
Something I really didn't like about the UK and that I have, in great detail, already told many people about against their wishes was public transport.
Firstly, ownership of the transport companies plays a big role. It sounds odd, but for example: my city has a 100% stake in the public transport company running out trams and buses. This allows them to have their say in how public transport is ran, which is important since the politicans (ideally) want good transport options for their citizens.
![German illustration about public transport passengers being eligible voters from the magazine Der Spiegel. It depicts a huge amount of passengers crammed into an overfull bus as well as a long line of people still trying to get on the bus.](fahrgaeste.webp)
<ImageSubtitle content="The headline translates to: 'Small tip for big politicians: 15 millions passengers are 15 million voters'" />
In the UK, however, the buses are almost always privatised, and so it feels as if the bus companies are weighing profits too high and customer care too low.
I found it particularly strange when cities have more than one company operating their network, especially since these companies usually don't exactly cooperate. Portsmouth had two First Bus and Stagecoach and it's just such a mess. No one publishes a proper map for their services, all their apps are rubbish and of course don't help you for services provided by other companies, the tickets are different between providers and have different validity areas, timeframes, lines you can take... ugh...
Trying to learn the routes was so annoying because no one provided a half-decent map. Stagecoach publishes one of just their services but without individual stops listed. The city of Portsmouth provides a combined map of all services, but it's extremely hard to read as all lines are coloured green, so they effectively mark streets where buses run on, but not the stops they actually serve, which is nonsensical because some lines run routes that suggest you can change over to another service, but they don't actually stop anywhere near the crossover section. One of the worst offenders for this is the 18, which runs on the roundabout between Pier Road and Duisburg Way where it crosses the lines 23 and 25, but it **just runs straight through with no stop!!** What fucking idiot came up with this?? Stagecoach runs all of these lines themselves! Oh and, First Bus can't be arsed to provide *any* map whatsoever. None at all. They should genuinely be embarrassed.
The services operated really weirdly as well. I saw many instances of bus lines from different operators directly competing for passengers. And as if that wasn't already bad enough; buses compete for passengers with other bus lines and with **trains** too!! Portsmouth had this too, with the First Bus X4 and X5 lines to Southampton as well as the Stagecoach 700 to Chichester. Serving stops in-between major train stations is important, sure, but the trains stopped really frequently anyway almost as if they were genuinely viewing the buses as competition...
I was particularly badly hit by the lack of good public transport options. Langstone Campus was served by only two lines: the 13, and the U2.
The 13 ran hourly, but it felt as if the timetable was seen as a recommendation, given that it liked to show up 10 minutes late or even 5 minutes early, and you can never really predict what it'll be. The 13 fucked me over particularly with its early departures, which meant I frequently had to wait for the next 13 in an hour or walk. Plus, the 13 didn't run at all on some days during the holidays. No service whatsoever. Apparently, however, this is a normal thing in the UK and even the **LONDON TUBE COMPLETELY STOPS RUNNING** around Christmas! How one of the biggest cities on this planet can handicap itself this badly, I will never understand. Run a reduced service, of course but **NO SERVICE, WTF??**
The U2 was even worse. It was free but I wouldn't have wanted to pay for a service this atrocious anyway.
First off, it ran every 50 minutes. *Fifty!* For one, this was insanely inconvenient. Also, this didn't even line up with the university timetable, as all lectures (as far as I could tell) started on the hour.Considering the U2 is a **university bus line**, I cannot grasp how the university can willingly pay to run a service this awful. Having a lecture at 14:00 was the worst, because it meant you'd have to take the bus at 12:50. That's 70 minutes before the lecture starts, for a route that takes 19 minutes. And if your bus didn't show up, as had happened to me tough luck, you're missing your lecture. As an added bonus, the 50-minute frequency meant that layover times at the end stops were too short to catch up with the frequent delays, or just give the drivers any kind of rest, really.
![Interior view of a First Bus on the U2 service](u2interior.webp)
Seccondly, the bus was terrible. They always ran with the oldest bus First Bus had, which would *vibrate heavily* and loudly all throughout the ride. It was also woefully undersized for some of the crowds, especially considering the U1 got electric double deckers running every 15 minutes despite the fact that *almost no one was using them*. Having only a single door where everyone had to scan their ticket made call times at certain stops extremely long too, as you'd have to wait for everyone to get up which in Portsmouth they apparently don't do until after the bus driver has opened the door then leave, then everyone has to get on, scan their tickets...
Thirdly, the bus doesn't have announcements! Or at least it didn't. After I wrote a *very* long e-mail to the university estates team about how genuinely awful their bus service was, they pretty much ignored everything I said, *but* they silently introduced announcements! I was shocked when I saw and heard them on the U2. A standard feature on literally any bus, literally absent from this bus, but finally no more?
Kind of. In the end, only like a third of the buses had the announcements. They also hardly worked. From day to day, new issues would arise where the displays skips stops or even introduces new ones that aren't actually served by the line.
![Close-up of the U2 university bus heads-up display showing the next few stops](u2hud.webp)
Oh and, the buses didn't run at all during the entire holiday period a *month*. The last bus of 2025 ran on the 19th of December, and the first bus in 2026 ran on the 12th of January. It seems that no one at the university cares about the students living in halls during the holiday period. I was very negatively affected by this, since Langstone campus is *so* remote; the nearest supermarket is a 25-minute walk away.
It genuinely felt as if the university either didn't care about its students, or they are trying to get rid of the buses. The latter I definitely think to be true; apparently it's normal in the UK to have students pay for the university buses (such bullshit). Portsmouth is the exception, but even they are giving up on this, as staff now has to pay for the Portsmouth university buses. Plus, the uni's estates team complained about the cost of running the buses when I contacted them (duh, how about don't build a campus on the other side of the island?). I'm assuming they are trying to actively reduce ridership so that it sinks to a level where they can 'justify' cutting the service entirely.
![Two Routemaster buses parked in front of Euston station in London](routemasters_euston.webp)
My experiences outside of Portsmouth weren't exactly great either. Cardiff charged me nearly 3€ for riding five stops on the line 6, and even London's Routemasters weren't that great. I only took one on the line 68 to get from Waterloo to St. Pancras International, but the ride felt representative of the London bus experience.
For example, the driver wasn't unfriendly, but definitely quite impersonal. When I asked him whether my tap-on had gone through, he only communicated in hand gestures, ending with a thumbs-up. Although I guess that was all the information I needed, so it can't have been too bad.
The interior of the bus was worn, not at all how I'd expect London's premier buses to look. Functional, but not a super pleasant experience. It was also a double decker, of course...
Okay, not sure if this is a hot take, but I dislike double decker buses. I do think the concept is quite cool, and I do like sitting up top for the view. However, they're inaccessible, they lengthen call times at stops, and it's just really hard to get up or down, especially when the bus is moving! I found them to be an even bigger issue in Portsmouth, where I frequently saw double deckers that were completely empty on the upper deck, with everyone sitting downstairs. A significant chunk of ridership in Portsmouth is seniors probably because a) the buses are unattractive for anyone needing reliable transport, and b) they can ride for free and they quite literally cannot access half the bus.
I did appreciate the flat £1.75 fare on London buses though probably an incentive to take some pressure off the tube, I imagine. Way better value than Portsmouth's £3 "capped" fare.
## The Return Trip
<!-- swr not really offering luggage spaces, had to sit next to the toilet
eurostar 45min delay fuck you, missed my connecting train and had to wait 2h for the next one in brussels. -->
After being away from Germany for so long, was I glad to see ICEs and other German trains again.
![An ICE 3neo calling at Cologne Central Station, Germany, covered in some snow](ice3neo.webp)
<ImageSubtitle content="The snow was the icing on the cake." />
What's crazy is how positively I raved about German public transport. We are *by no means* perfect. There are lots of issues especially in rail transport, and there are a lot of improvements to be done in local too. But in comparison to the UK, it's literally night and day. Like the fact that the bus right in front of my house runs every 7.5 minutes from 6:00 to 18:00, and still pretty frequently outside of those hours. Or that, if I don't want to take that bus, I can take the tram that's right nearby as well, and in a year's time, there will be a second tram line running here. Or that we have the '[Deutschlandticket](https://deutschlandticket.de/)' that allows us to use virtually all of Germany's public transport without trying to figure out ticket validity zones and whatnot. Or that we have way more than just buses trams, S-trains, underground trains and they're not just reserved for the capital. Even our damn ferries are cheaper by orders of magnitude.

View File

@@ -112,7 +112,7 @@ I didn't have any kind of clear goal for today, but I did enjoy the sketching. A
For the most part, I was just testing thing without sticking to just faces. Most importantly perhaps: no perfectionism! I didn't use my eraser *once!* The focus was on trying more rather than perfecting one single drawing. It did work to my benefit, in a way; I drew bodies, heads, even hands. For the most part, I was just testing thing without sticking to just faces. Most importantly perhaps: no perfectionism! I didn't use my eraser *once!* The focus was on trying more rather than perfecting one single drawing. It did work to my benefit, in a way; I drew bodies, heads, even hands.
I talked to a friend of mine earlier today about drawing. Thing is, she's a pro at it. She's been donig it for ages, she's studying art too, and she does insanely cool paintings. She did give me quite a few tips when we talked, but I did notice that a bit of it was way above my level. At one point, she started sketching a realistic eye, telling me about minute details like the positioning of eyelashes, and while I didn't see a direct connection between her advice and my drawings, I listened and watched in awe as she drew a really good-looking eye from what I assume is just experience and possibly my eyes as a partial reference. I talked to a friend of mine earlier today about drawing. Thing is, she's a pro at it. She's been doing it for ages, she's studying art too, and she does insanely cool paintings. She did give me quite a few tips when we talked, but I did notice that a bit of it was way above my level. At one point, she started sketching a realistic eye, telling me about minute details like the positioning of eyelashes, and while I didn't see a direct connection between her advice and my drawings, I listened and watched in awe as she drew a really good-looking eye from what I assume is just experience and possibly my eyes as a partial reference.
We also talked about perfectionism and how it can be beneficial to draw without erasing constantly like how I drew [my drawing from two weeks ago](/blog/2026/0129/) using a ballpoint pen instead of a pencil. No going back using that. I did intend to use a gel pen today, but I kind of shied away from it for now. Thinking about it now, a ballpoint pen may be better actually, because it draws finer and less bold lines. We also talked about perfectionism and how it can be beneficial to draw without erasing constantly like how I drew [my drawing from two weeks ago](/blog/2026/0129/) using a ballpoint pen instead of a pencil. No going back using that. I did intend to use a gel pen today, but I kind of shied away from it for now. Thinking about it now, a ballpoint pen may be better actually, because it draws finer and less bold lines.
@@ -482,13 +482,7 @@ Hand 1 has pretty thick fingers, but it was my first try so whatever. Hand 4's f
Went pretty well! No idea what I'll do tomorrow, but it's going well. If I'm really daring, I'll do another character (that'll hopefully take less time...) and try to add some cool hands. Jazz hands would be funny ngl Went pretty well! No idea what I'll do tomorrow, but it's going well. If I'm really daring, I'll do another character (that'll hopefully take less time...) and try to add some cool hands. Jazz hands would be funny ngl
Oh a bit of a tangent: I do have to say that I felt a little down today. There were a lot of good things today: I'm virtually sickness-free, I wore a cool new outfit and felt very gen-Z-internety in it so I listened to I Love My Computer by Ninajirachi on the bus and felt even more in my element, and the weather was super nice today as well. I even met up with friends! Oh a bit of a tangent: I just looked at my drawing from [two days ago](#day-18) again, and that made me feel really good. I'm *so happy* about that drawing in particular and my progress in general. *I created these drawings!!* How cool is that?? AI images could never evoke the joy in me that I am feeling when I look at my sketches.
But I wanted to show one of my friends, the artsy one, my drawings today. I don't see her super often so I wanted to use the chance. I was actually really excited to do so because obviously I've put quite a lot of time into these drawings and I'm quite proud of especially what I've managed to accomplish in this short time, and I like sharing that. But she wanted me to show them off while we were busy doing something else entirely, to which I told her something along the lines of *"I'll show you when we have the time for it"* because I wanted to take my time with that rather than rush through the sketches. I ended up not being able to show her my drawings, but she still gave me the advice to *study anatomy*.
That felt pretty discouraging. She hadn't even seen my drawings, doesn't know what or how I am drawing, and gave me pretty boilerplate advice as a result. I guess it's not too dramatic, but it still didn't feel great.
But I'm fine now. In fact, I just looked at my drawing from [two days ago](#day-18) again, and that made me feel really good. I'm *so happy* about that drawing in particular and my progress in general. *I created these drawings!!* How cool is that?? AI images could never evoke the joy in me that I am feeling when I look at my sketches.
## Day 21: we'll see creation come undone ## Day 21: we'll see creation come undone

View File

@@ -0,0 +1,247 @@
<div class="callout-warning">
<p><b>Note</b>: as I'm constantly learning new things, this blog post is sort of outdated now. The information is still correct, but it feels inefficient and leads into a dead-end. I dislike this, as I wanted to write a guide that provides an expandable base. Therefore, I'll likely either update or replace this article and trim it down to focus on the important bits.</p>
<p>Expect a Go API.</p>
</div>
*Hey!*
Do you want to develop a web application in SvelteKit? Do you want this application to access a PostgreSQL database on a remote server? Do you struggle with CORS (cross-origin resource sharing) and CSRF (cross-site request forgery) and keep receiving error 403? Well, I did, and I couldn't find ***ANYTHING*** on the internet going beyond a test running locally. This is why I'm writing this guide.
Let me preface this by saying: what I did isn't perfect. It's also likely incomplete; I would give bad advice in a complete guide for sure. What this is, is a guide covering the key aspects for setting up a relatively minimal setup to get your application online successfully, upon which you can expand by adding more features. The important distinction from other guides I've found online is that this guide doesn't deploy the backend locally, but instead sets it up online, ready to use.
## The Guide
### What You Need
- a device, such as a laptop or a desktop, with the following installed:
- an IDE
- `npm`
- `ssh` keys for the server, if you're accessing it remotely
- a remote server instance set up with some software:
- Nginx
- Docker Compose
- an idea for what kind of app you wanna make
### Getting Started
#### SvelteKit
For creating a SvelteKit app, I recommend the guide [Getting Started](https://svelte.dev/docs/svelte/getting-started) by the Svelte team. You won't need more than the first four commands to create the app and get it up and running. During the setup, I recommend you pick the `adapter-node`.
#### Backend
For the backend, which will run on your remote server, it's very easy to use Docker Compose to get started. Once you're `ssh`'d into the server (or physically sitting at the terminal), create a folder where the data will live in, then create a file named `compose.yml` (for instance, by typing `nano compose.yml` into the terminal) and paste this into the file, then use `docker compose up -d` (or if that doesn't work, try `docker-compose up -d` with a hyphen) to start the container:
<pre class="code-block">
version: '3'
services:
api:
image: postgrest/postgrest
restart: always
ports:
- 3100:3000
environment:
PGRST_SERVER_HOST: '0.0.0.0'
PGRST_DB_URI: 'postgres://authenticator:password@database:5432/postgres'
PGRST_DB_SCHEMAS: 'api'
PGRST_DB_ANON_ROLE: 'web_anon'
PGRST_SERVER_CORS_ALLOWED_ORIGINS: "https://app.natconf.dev"
depends_on:
- database
database:
image: postgres
restart: always
ports:
- 5432:5432
environment:
POSTGRES_USER: 'username'
POSTGRES_PASSWORD: 'password'
POSTGRES_DB: 'database_name'
volumes:
- ./postgresql:/var/lib/postgresql
</pre>
Important notes:
This `compose.yml` covers both the PostgreSQL database itself as well as a program called [PostgREST](https://docs.postgrest.org/), which connects to a PostgreSQL database and acts as a [REST](https://developer.mozilla.org/en-US/docs/Glossary/REST) api. This is advantageous, as it means you won't need modules such as [Postgres.js](https://github.com/porsager/postgres) to send raw SQL queries from SvelteKit to the database. With PostgREST, you can use a regular URL to access and modify database data using HTTP verbs such as GET (for getting data) and POST (for sending new data).
Some environment variables need to be set: `PGRST_SERVER_CORS_ALLOWED_ORIGINS` decides from which domains requests are allowed. Setting it to `"*"` allows all domains. Change this to the domain your application is running on. Set `POSTGRES_USER` and `POSTGRES_PASSWORD` to the data you'll use to log in as your admin account. Also, `PGRST_DB_ANON_ROLE` is the user that will be used when making requests to the database without credentials. This user's permissions will decide whether GET, POST, etc. requests will go through. You don't need to change this, but if you do, you'll also have to change `web_anon` later in this guide.
In `PGRST_DB_URI`, the link uses the username `authenticator` and a password. Replace the word 'password' with the actual password you'll use for the `authenticator` role. You will set up this role [later](#configure-the-database). This is *not* the same password you'll set for `POSTGRES_PASSWORD`!
The ports may differ on your machine. The `database` service runs on port 5432. You only need to change this if this port is already used by something on your server. Keep in mind to change the **left** number only! The left number is the port that faces outwards, the right port is the one used internally by the container. Since PostgreSQL is running on port 5432 by default, and since there is nothing else running inside the container that's also on port 5432, we don't need to change the right side at all. If you change the left port, be sure to change the port used in the `api` service's `PGRST_DB_URI` as well, as this URI is used by PostgREST to connect to the database.
I did change the port for the `api` service, however. PostgREST runs on port 3000 by default. This is problematic, as Node (which we will use for the SvelteKit application later) also runs on port 3000 by default. I chose to change PostgREST's port to 3100 here to avoid conflict, but you can pick any non-privileged port (any between 1024 and 65535 is good).
Keep in mind that you will not need to open up any of these ports on your firewall. Why? This is why:
#### Nginx
I'm using Nginx as a reverse proxy here. What does this mean? It means that we can send a human-readable URL to Nginx, such as this: `https://files.natconf.dev`, and Nginx will *internally* route us to the place the service is actually running on: `http://localhost:3923`. This has several advantages: as the end user, we won't need to remember the IP address or the port of the services we connect to. As the server administrator, we will only need to expose a minimal number of ports. In this example, port 3923 is actually closed off from the public, meaning that bypassing Nginx and connecting to the service directly, e.g. by typing `https://natconf.dev:3923`, is impossible. The only open port is 443, which is a standard port for websites served over encrypted HTTPS. Your browser will always try to connect to a website starting with `https://` on port 443, unless you specify another port. Port 80 is actually also open on my server, as it accepts requests through the unsafe `http://`, but it only redirects to `https://` on 443.
I configured Nginx to run both the SvelteKit app as well as the PostgREST backend from the same subdomain. You don't *need* to do this, and I *think* it should even work if you put, for example, the database on a different subdomain, but I can't guarantee it.
<!-- &#123; == {. if this is not encoded, sveltekit will try to interpret it as code and throw an error -->
<pre class="code-block">
server &#123;
listen 443;
server_name app.natconf.dev;
location / &#123;
proxy_pass http://localhost:3000;
}
location /customers &#123;
proxy_pass http://localhost:3100/customers;
}
}
</pre>
This `server` block serves two functions:
- it serves our SvelteKit app, which by default runs on port 3000, from the root directory of the subdomain (e.g. `https://app.natconf.dev`), and
- it serves the table `customers` from our PostgreSQL database, which is accessible through PostgREST on port 3100, on the subpage `/customers` (`https://app.natconf.dev/customers`).
- If your table has a different name, replace `customers` with that table's name.
- If you want multiple tables to be accessible, copy the entire `location /[tablename]` block and paste it in as often as you need, replacing `[tablename]` with the names of the tables.
Be sure to change `natconf.dev` to your own domain, as this will otherwise not work.
### Configure the Database
Once you have the database running, we need to create a table to store data in. You can log into the database on your server by running this command to enter the PostgreSQL console: `docker exec -it [service-name] psql -d postgres -U [username]`. `[container-name]` is the name of the PostgreSQL service, which you will see popping up in the terminal after running `docker compose up -d`. It will likely look something like `foldername_database`. `[username]` is the name of the privileged user defined in `compose.yml` under the environment variable `POSTGRES_USER`. This is basically your admin account.
You're best off following [the official guide for PostgREST](https://docs.postgrest.org/en/v14/tutorials/tut0.html#step-3-create-database-for-api). Important steps: create the schema, name it `api` (that's the publicly-accessible schema because we set `PGRST_DB_SCHEMAS: 'api'` in the `compose.yml`), create a table (name has to start with `api.` but can be anything after that, e.g. `api.customers`), insert test data, create the roles `web_anon` and `authenticator` and then quit by typing `\q`. That's all. Welcome back!
You can now access the database data on `https://app.natconf.dev` (if you replace my domain with yours). Keep in mind that the PostgREST guide only grants read access (GET) to the database; in order to POST data to the database, you need to grant `INSERT` privileges to `web_anon`: `grant insert on api.customers to web_anon;`.
If you want a kind voice to guide you through this process, I found [Ian Wootten's video on setting up PostgREST](https://youtu.be/RxuofiZNhtU) to be very nice to follow along.
### POST Data From SvelteKit
The difficult part is done. Sending data from SvelteKit to be inserted into the database is pretty easy, fortunately. You can use `fetch` to do this:
<pre class="code-block">
const response = await fetch(`https://$&#123;API_HOST}/$&#123;API_DB}`, &#123;
method: "POST",
headers: &#123;
"Content-Type": "application/json",
},
body: JSON.stringify(customer),
});
if (response.ok) &#123;
console.log("success");
} else &#123;
console.log("an error occurred: " + response.statusText);
}
</pre>
In this example, I set up two environment variables: `API_HOST` and `API_DB`. Those are secrets that won't be published to your Git provider when you push your changes (if your .gitignore is set up correctly!). `API_HOST` is the fully-qualified domain, e.g. `https://app.natconf.dev`, and `API_DB` is the name of the table, e.g. `customers`. Doing this is not necessary, but it's good practice to keep secrets, especially once you get to the stage of needing passwords or authentication keys. If you want to use environment variables, create a file named `.env` in the root directory of your application, add the variables as such:
<pre class="code-block">
API_HOST="https://app.natconf.dev"
API_DB="customers"
</pre>
...and then import them into your script:
<pre class="code-block">
import &#123;
API_HOST,
API_DB,
} from '$env/static/private';
</pre>
Keep in mind that the `fetch` call is async, meaning that if you wrap this in a function, you will have to declare the function as such, e.g. `async function insertCustomer(...)` and call it using await: `await insertUser(...)`.
Also, the `customer` variable I declared is basically one database entry with the exact same properties as you set it up in PostgreSQL, except for the primary key (likely named `id`). You shouldn't send this in a POST request, as `id` will be auto-generated by the database to be a unique value. That is, unless you *want* to generate these values yourself, in which case, go ahead.
This should now work fine to POST data to the database...
### Fix Error 403 On SvelteKit
...except in production. Once you deploy the application on your server, you may face error *'403: Cross-site POST form submissions are forbidden'*. This is an issue caused by a security measure implemented in the browser that forbids accepting data unless the server sends the correct headers. You will only see this issue in a production build as SvelteKit disables these security measures when you're running a development build. The way I fixed this in production is by adding the domain of my backend to the `trustedOrigins` inside the `kit` block of my `svelte.config.js`:
<pre class="code-block">
kit: &#123;
adapter: adapter(),
csrf: &#123;
trustedOrigins: [
"https://app.natconf.dev"
],
},
},
</pre>
**This is the crucial bit.** Only by adding the URL of my backend to the `trustedOrigins` was I able to fix the 403 errors. Is this the correct way of doing it? Frankly, I don't know. But I do know that you should definitely not put `"*"` as one of the trusted origins, as this would mean your app accepts data from anywhere, and that is *definitely* unsafe.
If you just came here to get a solution for the 403 error that's it! We're done :) But read on for an extra piece of advice as well as my struggle to get this working.
### Your Data Will Be Public
**Important:** the contents of the database will be publicly visible, as we've granted GET (and possibly INSERT) permissions to `web_anon`. You will need to do a little more work to create a safe API that only exposes required data. Use either [PostgREST's guide](https://docs.postgrest.org/en/v14/tutorials/tut1.html) or [Ian Wootten's video](https://youtu.be/RxuofiZNhtU) for guidance on how to restrict and grant access safely.
## The Rant
If you only came for the guide, you've finished it. What follows is me ranting about how difficult it was to get this information.
### Why Did I Write This?
I was building a small SvelteKit application for a project I'm doing together with a friend. I figured it'd be easy, as it's just like building my website, except I'll also make some requests to a backend on my server. I was right... kind of. I have spent *two days* trying to figure out how to do this successfully.
The requests were working fine when I was running the development build of the SvelteKit app on my PC. Once I deployed it though, I was facing *'403: Cross-site POST form submissions are forbidden'* errors for every single request. I couldn't figure out why. I scoured the internet for answers, trying anything I could. I configured `Access-Control-Allow-*` headers in Nginx. I set the `ORIGIN` parameter in the app's `.env`. I ran the app and the database on the same subdomain. Nothing worked. When I tried looking for tutorials, there was *nothing* explaining how I could set up what I had in mind.
How could this be? I'm doing web development 101 here! This isn't some advanced toolchain I'm using, it's the bare minimum for a working application with a backend! Somehow, no resource I could find covered this. Any website I read and any video I watched only ever ran those services locally. Docker containers were spun up on the development client, the client apps were run in development mode (which disables CORS/CSRF security features), and there was never a *live* example given.
### Stupid Gaslighting AI Slop
These days, when looking up tech problems in particular, most of the online resources are AI-generated slop. It was genuinely extremely difficult to avoid them, as they were not just prevalent, but almost entirely dominating the search results. There was this one page that I particularly despised. The worst part? It was actually several pages.
If you stumble upon a page such as `w3tutorials.net` or `tutorialpedia.org`, *run*. Do not take in their information. Avoid at all costs. In fact, these pages both use the very same CSS styling with their dark blue background and fade-in title animation upon page load, and you will find even more domains using this exact same template.
But why run? Is it really that bad? Maybe the information is useful?
I'm going to prove why you should avoid generative AI at all costs. Let's go through the article I read: "How to Fix 'Cross-site POST Form Submissions Are Forbidden' Error in SvelteKit: Dev Server Works, Build Fails". Extremely long title, but it in fact describes my exact problem. Four 'fixes' are given:
1. Use Relative Form Actions
This 'fix' describes using relative paths to access form URLs. Instead of `https://app.natconf.dev/customers`, I should just use `/customers`.
This doesn't work.
2. Include the CSRF Token
This 'fix' explains that the issue may be caused by the lack of a CSRF token included in the form that would verify the validity of the submission. The solution is to import the token into the app using `import { csrfToken } from '$app/stores';` and then including the token in a hidden `<input>` named `_csrf`.
*What in the hell* did this AI smoke to hallucinate this? There is no such thing as an importable CSRF token, and it wouldn't make sense for the solution to just be 'send the token along with the request'. Who's validating the token? How would they know it's valid? You would need server-side validation of the token, which is a real thing, but you'd need to code that yourself or use a library for it. You can't just send a random string and say "this request is real, here's some random text to prove it".
3. Configure `ORIGIN` and Environment Variables
Ignoring the fact that their markdown styling doesn't work correctly and shows the \` marks on text that is supposed to be monospaced, this 'fix' alleges that setting the `ORIGIN` environment variable needs to be set to the domain the application is running on so that SvelteKit is aware of this domain.
This *may* work? SvelteKit documentation does [make mention of an `ORIGIN` variable](https://svelte.dev/docs/kit/adapter-node#Environment-variables-ORIGIN-PROTOCOL_HEADER-HOST_HEADER-and-PORT_HEADER) used in the Node adapter. However, as far as I could tell, this had no effect on my app.
4. Check Server Hooks (Handle Function)
This 'fix' accuses you, the developer, of explicitly deleting the required headers for CSRF validation using code such as: `delete event.request.headers.origin;`. *Excuse me?* You little piece of shit. How dare you accuse me of something like this? No, of course I haven't explicitly deleted the headers from my request!
Actually, now that I think about it, this is a very odd thing for an AI to produce. Usually, they are overly positive towards the user. I guess this one was still positively phrased and only indirectly accusing the reader of the mistake. Still, the implication is ridiculous.
### No Surprise Stack Overflow is Dying
Alas, I couldn't find anything helping my cause. In my most desperate moment, I came up with an idea I never thought I'd pursue: *asking a question on Stack Overflow*. **THE** Stack Overflow! The place where arrogant people get high on the most minuscule amounts of power or so I've heard. It can't be that bad.
I wasn't entirely sure whether my problem lied in my SvelteKit, Nginx, or PostgreSQL setup, so I documented them briefly but with the necessary detail in my question. I described my plan and my previous attempts at fixing the issue at hand, including links to the resources I had used. I formatted the entire thing and structured it so that it would be relatively simple to digest. It also had a concluding question to summarise my problem at the end. I read their entire stupid article on 'writing a good question'. What a ridiculous article.
I submitted it to the Staging Ground, which is apparently a Stack Overflow-exclusive thing where new users are forced (?) to submit their questions for 'experienced' users to judge them (which is such a fucking weird concept) and give advice. Fair enough, I thought, I have a fairly open mind and I'd gladly receive constructive feedback so that others could answer my question better.
What instead happened was that within less than 10 minutes of posting my question, two users deemed my question 'off-topic' with the reason 'this question is not about programming or software development' and closed it before it went public. wow. One of them at least had the decency of giving me the advice to post in either Server Fault or Database Administrators. Seeing that this *definitely* wasn't a database problem, I deemed the latter suggestion misguided at best, but I did try Server Fault instead. I copy-pasted my question, submitted it directly (they had no such thing as a Staging Ground), and proceeded to receive no answers.
The best part: once I figured out the solution to my problem on my own, it became abundantly clear that my problem was caused by SvelteKit. This means:
- the question was actually off-topic on Server Fault
- the question was indeed correct to be posted on Stack Overflow
- whoever judged my question to be 'off-topic' likely only wanted to exercise their virtual 'power' and didn't even bother reading and understanding my question while expecting me to read paragraphs on 'how to ask a good question'
You know what? Stack Overflow can burn in hell. No wonder its user base is continually shrinking. What a horrible, toxic place. I'm saying this mostly because of the things I've *heard* about Stack Overflow, but my short first-hand experience clearly showed that those things are true.

View File

@@ -0,0 +1,32 @@
<p class="lightyears-text">The world looks so different now. The world looks so different now. The world looks so different now.</p>
## Background
One of my favourite music artists, [Jaron](https://youtu.be/GXvqQ5-P82I), released his album LIGHTYEARS a little over a year ago. For his visuals, he uses a variety of symbols in place of Latin letters, and there's a converter on [his website](https://jaronsteele.com/) too. Only problem is that my browser can't seem to display most of the characters because the characters aren't included in most fonts.
That's why I made a font! It allows you to type Latin characters from `A-Z` as well as numbers `0-9` and `!?` in the LIGHTYEARS style. Like this:
<p class="lightyears-text">trans rights!</p>
The font exclusively uses characters from the Noto font family. Many of the Noto varieties have been stitched together to recreate the whole LIGHTYEARS alphabet.
To create the font, I used [FontForge](https://fontforge.org). Finding this tool was both a blessing and a curse, as it was exactly what I needed, but it kept. crashing. all. the. time. I tried both the AppImage as well as the release on `dnf` and both had the same issues. I managed to make it work, but it took a lot of patience. Eventually I figured out that importing Noto Maths gave me a 3-8 second window before the editor crashed. The project file would forget the imported font, but if I had copied any glyphs it would keep those.
## Download & use
[Download the font here](https://files.natconf.dev/public/lightyears.woff2). It's in the web-optimised `woff2` format and has most characters stripped to minimise its file size it's less than 20 kilobytes in size! Uppercase and lowercase letters are the same.
For use on your website, put the font into your resources/static/similar folder and then add this block of code to your CSS file:
<pre class="code-block">
@font-face &#123;
font-family: "LIGHTYEARS";
src: url("/fonts/lightyears.woff2");
font-weight: 400;
font-style: normal;
}
</pre>
Then you can change any element's font by setting `font-family: 'LIGHTYEARS', sans-serif;`. Because it's the Latin characters and Arabic numbers that have been changed, you can type text in regular English and people can 'decrypt' the messages by copy-pasting the text somewhere else!
You do **not** need Jaron's converter to type stylised text! The converter returns the actual symbols from other scripts, whereas this font only changes how letters look.

View File

@@ -0,0 +1,7 @@
I'm switching my website domain from `denizk0461.dev` to `natconf.dev`!
All I can say for now about this change is that I came up with this domain name a while back and I liked it more so than `denizk0461.dev` , so I've been thinking about migrating.
I'm updating things as I go. All links on this website now point to the new domain, and all services have been moved over too. As of right now, they're also still accessible via the old domain, but I will be disabling that soon. Instead, I'll set up redirects, which will be in place until the domain expires on 2026-06-04.
I *may* change up some visual elements in the process, but it won't be a major redesign.

View File

@@ -4,7 +4,7 @@ import { posts } from "../posts";
const xml = () => `<rss version="2.0"> const xml = () => `<rss version="2.0">
<channel> <channel>
<title>denizk0461's Blog</title> <title>denizk0461's Blog</title>
<link>https://denizk0461.dev/blog/</link> <link>https://natconf.dev/blog/</link>
<description><![CDATA[denizk0461 blogs about stuff here]]></description>${getEntries()} <description><![CDATA[denizk0461 blogs about stuff here]]></description>${getEntries()}
</channel> </channel>
</rss>`; </rss>`;
@@ -16,8 +16,8 @@ function getEntries(): String {
<item> <item>
<title><![CDATA[${entry.post.title}]]></title> <title><![CDATA[${entry.post.title}]]></title>
<description><![CDATA[${entry.post.description}]]></description> <description><![CDATA[${entry.post.description}]]></description>
<link>https://denizk0461.dev/blog/${entry.key}</link> <link>https://natconf.dev/blog/${entry.key}</link>
<guid isPermaLink="true">https://denizk0461.dev/blog/${entry.key}</guid> <guid isPermaLink="true">https://natconf.dev/blog/${entry.key}</guid>
<pubDate><![CDATA[${new Date(`${entry.post.date}T${entry.post.time}`).toUTCString()}]]></pubDate> <pubDate><![CDATA[${new Date(`${entry.post.date}T${entry.post.time}`).toUTCString()}]]></pubDate>
</item>`) </item>`)
entries.forEach(entry => { entries.forEach(entry => {

View File

@@ -23,13 +23,39 @@ export interface BlogPostLink {
export const posts: BlogPostLink[] = [ export const posts: BlogPostLink[] = [
// ["2026/0128", { {
// date: "2026-01-05", key: "2026/0326",
// time: "13:00", post: {
// banner: "langstone.webp", date: "2026-03-26",
// title: "Portsmouth Postmortem", time: "20:50",
// description: "", banner: "banner.webp",
// }], bannerAlt: "White light blurs on a darker background.",
title: "Moving On",
description: "It's time to switch domains.",
}
},
{
key: "2026/0325",
post: {
date: "2026-03-25",
time: "22:22",
banner: "banner.webp",
bannerAlt: "A sunset captured from an Autobahn exit.",
title: "I made a LIGHTYEARS font",
description: "I feel electric and it's only getting brighter!",
}
},
{
key: "2026/0317",
post: {
date: "2026-03-17",
time: "17:00",
banner: "banner.webp",
bannerAlt: "A Microsoft Surface Pro 8 displaying a Blue Screen of Death.",
title: "How To: Set Up SvelteKit Frontend + PostgreSQL Backend",
description: "How to set up a web application with a backend on a remote server without getting error 403.",
}
},
{ {
key: "2026/0214", key: "2026/0214",
post: { post: {
@@ -86,9 +112,3 @@ export const posts: BlogPostLink[] = [
} }
}, },
]; ];
// export function getDate(post: BlogPostDetails): string {
// var s = [post.year, post.date.split()].join("");
// s.
// return "";
// }

View File

@@ -78,7 +78,7 @@
}, },
{ {
text: "Bluesky", text: "Bluesky",
link: "https://bsky.app/profile/denizk0461.dev", link: "https://bsky.app/profile/natconf.dev",
}, },
{ {
text: "Codeberg", text: "Codeberg",
@@ -151,7 +151,7 @@
<img alt="Screenshot of the taskbar of a Fedora KDE setup. There are multiple icons. From left to right: Clank as the application launcher icon, Firefox, fooyin music player, Dolphin file explorer." src="taskbar.webp"> <img alt="Screenshot of the taskbar of a Fedora KDE setup. There are multiple icons. From left to right: Clank as the application launcher icon, Firefox, fooyin music player, Dolphin file explorer." src="taskbar.webp">
<p>As for the server infrastructure: the website is hosted on a Hetzner server instance I am renting. It's a relatively cheap CPX22 node that costs me 7,72€ a month, and besides my website, it's also hosting things such as a Nextcloud instance and a Minecraft server. In order to host and update the website, I wrote a small bash script that pulls the changes from the <a href="https://code.denizk0461.dev/denizk0461/pages">Git repository</a>, builds the website as a Node server, and then exposes it via Nginx.</p> <p>As for the server infrastructure: the website is hosted on a Hetzner server instance I am renting. It's a relatively cheap CPX22 node that costs me 7,72€ a month, and besides my website, it's also hosting things such as a Nextcloud instance and a Minecraft server. In order to host and update the website, I wrote a small bash script that pulls the changes from the <a href="https://code.natconf.dev/denizk0461/pages">Git repository</a>, builds the website as a Node server, and then exposes it via Nginx.</p>
<h4 id="software">Tools & Software</h4> <h4 id="software">Tools & Software</h4>
@@ -169,7 +169,7 @@
<p>The <a href="https://ratchetandclank.fandom.com/wiki/Ratchet">rat</a> in the bottom right of the screen is property of <a href="https://insomniac.games/">Insomniac Games</a>. Clicking it will bring you good fortune.</p> <p>The <a href="https://ratchetandclank.fandom.com/wiki/Ratchet">rat</a> in the bottom right of the screen is property of <a href="https://insomniac.games/">Insomniac Games</a>. Clicking it will bring you good fortune.</p>
<p>The style of the webring elements is adapted from a template provided by Rainbow Cemetery for the <a href="https://www.rainbowcemetery.com/devring/">Gamedev webring</a>. I adapted it into <a href="https://code.denizk0461.dev/denizk0461/pages/src/branch/master/src/lib/components/ring.svelte">a Svelte component</a> that allows setting the links, emojis, and arrows more easily.</p> <p>The style of the webring elements is adapted from a template provided by Rainbow Cemetery for the <a href="https://www.rainbowcemetery.com/devring/">Gamedev webring</a>. I adapted it into <a href="https://code.natconf.dev/denizk0461/pages/src/branch/master/src/lib/components/ring.svelte">a Svelte component</a> that allows setting the links, emojis, and arrows more easily.</p>
<h2 id="contact">Contact / Where to find me</h2> <h2 id="contact">Contact / Where to find me</h2>
@@ -177,7 +177,7 @@
<LinkList entries={links} /> <LinkList entries={links} />
<p>I removed my Discord username because I don't want to use Discord anymore, now that they <a href="https://www.zdnet.com/article/discord-age-verification-requirement/">ask for facial data and/or government ID that they force to be scanned by third-party AI</a> and then <a href="https://arstechnica.com/tech-policy/2026/02/discord-faces-backlash-over-age-checks-after-data-breach-exposed-70000-ids/">leak it...</a> Apparently they are also about to be <a href="https://www.zdnet.com/article/microsoft-may-be-poised-to-buy-its-next-community-discord/">bought up by Micro$lop</a>?? Discord is as good as dead to me.</p> <p>I removed my Discord username because I don't want to use Discord anymore, now that they <a href="https://www.zdnet.com/article/discord-age-verification-requirement/">ask for facial data and/or government ID that they force to be scanned by third-party AI</a> and then <a href="https://arstechnica.com/tech-policy/2026/02/discord-faces-backlash-over-age-checks-after-data-breach-exposed-70000-ids/">leak it...</a> Apparently they are also about to be <a href="https://www.zdnet.com/article/microsoft-may-be-poised-to-buy-its-next-community-discord/">bought up by Micro$lop</a>?? I prefer not to use Discord anymore, although I will admit I don't have any good alternative at the moment.</p>
<h2>Anything else?</h2> <h2>Anything else?</h2>
@@ -201,7 +201,7 @@
<hr> <hr>
<p><i>Last updated: 2026-02-25</i></p> <p><i>Last updated: 2026-03-26</i></p>
</Content> </Content>
<style> <style>

View File

@@ -9,7 +9,7 @@
link: "/blog/feed.xml", link: "/blog/feed.xml",
}, },
{ {
text: "Project N5 devlog", text: "Homesick devlog",
link: "/projects/projectn5/devlog/feed.xml", link: "/projects/projectn5/devlog/feed.xml",
}, },
] ]

View File

@@ -13,7 +13,7 @@
<p>This page uses <b>no cookies</b> as of now. No data will be stored on your device while browsing this website. <b>No trackers</b> are used either <b>no analytics</b>, not even a visit counter of any kind. Not by a third-party, and currently, none I built myself either.</p> <p>This page uses <b>no cookies</b> as of now. No data will be stored on your device while browsing this website. <b>No trackers</b> are used either <b>no analytics</b>, not even a visit counter of any kind. Not by a third-party, and currently, none I built myself either.</p>
<p>The Godot and Unity projects on the <code>apps.denizk0461.dev</code> subdomain <i>may</i> cache compiled shaders on your device, I'm not sure. These files would only be used by your GPU to render visual effects for the game they were compiled for.</p> <p>The Godot and Unity projects on the <code>apps.natconf.dev</code> subdomain <i>may</i> cache compiled shaders on your device, I'm not sure. These files would only be used by your GPU to render visual effects for the game they were compiled for.</p>
<p>Last updated: 2025-09-10</p> <p>Last updated: 2025-09-10</p>
</Content> </Content>

View File

@@ -1,6 +1,35 @@
import { type UpdateEntry } from "$lib/components/update-entry.svelte"; import { type UpdateEntry } from "$lib/components/update-entry.svelte";
export const entries: UpdateEntry[] = [ export const entries: UpdateEntry[] = [
{
date: "2026-03-26",
time: "20:50",
content: "<b>Important</b>: my website is changing domains.",
link: "/blog/2026/0326",
},
{
date: "2026-03-25",
time: "22:22",
content: "I made a LIGHTYEARS font!",
link: "/blog/2026/0325",
},
{
date: "2026-03-17",
time: "17:10",
content: "a bit uncharacteristic, but I wrote a guide on setting up a SvelteKit app + backend because I found NOTHING of the sort online.",
link: "/blog/2026/0317",
},
{
date: "2026-03-11",
time: "19:21",
content: "new page: my drawings!",
link: "/art/drawings",
},
{
date: "2026-03-07",
time: "14:12",
content: "I fancied up some element animations, especially on the devlog and blog pages! The main page also got some love.",
},
{ {
date: "2026-03-05", date: "2026-03-05",
time: "23:59", time: "23:59",
@@ -46,7 +75,7 @@ export const entries: UpdateEntry[] = [
date: "2026-02-03", date: "2026-02-03",
time: "15:48", time: "15:48",
content: "Now running my own Gitea instance! It now also hosts my website repository.", content: "Now running my own Gitea instance! It now also hosts my website repository.",
link: "https://code.denizk0461.dev/denizk0461/pages", link: "https://code.natconf.dev/denizk0461/pages",
}, },
{ {
date: "2026-02-02", date: "2026-02-02",

View File

@@ -1,180 +0,0 @@
<script lang="ts">
import Banner2 from "$lib/banner2.svelte";
import Content from "$lib/viewport/content.svelte";
</script>
<svelte:head>
<title>Uses | denizk0461</title>
</svelte:head>
<Content>
<Banner2
title="Uses"
banner="/projects/banner.webp"
bannerAlt="Closeup of the purple protagonist from Project N5"
subtitle="A list of the hardware and software I use for my creative tasks" />
<div class="container">
<div class="column">
<h2>Hardware</h2>
<ul>
<li>Self-built tower PC
<ul>
<li>Components include a Fractal Pop Mini Air case, an AMD Ryzen 5600 CPU, an Nvidia RTX 3070 graphics card, and 2 TB of Samsung NVMe storage</li>
<li>I also carried over the DVD drive from my old PC</li>
<li>Three monitors are plugged into my PC: a 32in Samsung M7 2160p smart monitor, a 24in 1080p Samsung TV from 2014, and a 24in 1200p 16:10 Dell monitor</li>
</ul>
</li>
<li>Lenovo IdeaPad Pro 5-14AKP G10
<ul>
<li>Currently running Windows 11, but I'm planning to install Fedora KDE!</li>
</ul>
</li>
<li>Logitech MX Master 3S mouse
<ul>
<li>Don't buy this. I kind of like the mouse, but the rubber is clearly taking a toll from regular use</li>
<li>Also, the left/right click buttons are cheap and WILL fail. I already replaced mine because the right click was far too unreliable (worked maybe 20% of the time). Replacing the microswitch requires soldering</li>
<li>Also also, it only has a 125 Hz polling rate, which is pretty low, especially when using a 120+ Hz display</li>
<li>As far as I read, the MX Master 4 is plagued by the same issues, except for the rubber, which was fortunately replaced with solid plastic</li>
</ul>
</li>
<li>Logitech MX Mechanical keyboard
<ul>
<li>I like this keyboard, despite the issues with the related mouse!</li>
<li>I should maybe have gotten the non-numpad version, because it is quite wide...</li>
<li>I do like having the numpad though</li>
</ul>
</li>
<li>Beyerdynamic DT 770 Pro headphones
<ul>
<li>Love these headphones. They're not the most stylish, but they sound <i>really</i> good</li>
<li>They're best suited for indoor use though</li>
</ul>
</li>
<li>Anker Nano 100W USB-C charger
<ul>
<li>I carry this with me everywhere. Since virtually all my devices use USB-C nowadays, I can charge anything using just this small brick</li>
</ul>
</li>
<li>Bambu Lab A1 mini 3D printer
<ul>
<li>Very easy-to-use 3D printer! Produces good results too</li>
<li>I don't really recommend it though. Bambu is pretty much the Apple of 3D printing; you'll be locked into their ecosystem</li>
</ul>
</li>
<li>Samsung Galaxy S20 FE
<ul>
<li>Bought this in 2021 and have no issues with it. Don't feel like I need a new phone anytime soon</li>
</ul>
</li>
<li>Samsung Galaxy Buds+
<ul>
<li>Reliable and good battery life despite their age, but one of the tips is starting to tear and the sound isn't that great anymore</li>
</ul>
</li>
<li>Sony DualSense controller
<ul>
<li>Gave away my PS5 but kept the controller. I like its weight and how it feels. USB-C is a big plus too</li>
</ul>
</li>
</ul>
</div>
<div class="column">
<h2>Software</h2>
<ul>
<li>Godot game engine
<ul>
<li>Switched over to this after the Unity runtime fee controversy in 2023</li>
<li>It's so enjoyable to use, I recommend it to pretty much anyone who wants to get into gamedev</li>
</ul>
</li>
<li>Blender 3D modelling suite
<ul>
<li>I'm more and more getting into 3D modelling, and Blender is really good for this</li>
</ul>
</li>
<li>Obsidian markdown editor
<ul>
<li>Switched over from Notion because I disliked having all my notes in Notion's proprietary standard and stored only in their cloud</li>
<li>Canvas is super cool for organising things in a pinboard style</li>
<li>The kanban plugin makes for a great TODO list</li>
</ul>
</li>
<li>Visual Studio Code
<ul>
<li>Great as an all-rounder code editor</li>
<li>This website is written using VS Code!</li>
</ul>
</li>
<li>Nextcloud
<ul>
<li>I self-host an instance as my personal cloud for syncing game assets and for tasks</li>
<li>I've been using tasks to organise myself a lot since 2015 or so</li>
</ul>
</li>
<li>paint.net
<ul>
<li>A simple-to-use image editor that I've been using for a very long time</li>
<li>Windows-only unfortunately</li>
</ul>
</li>
<li>Affinity image editing suite
<ul>
<li>For more sophisticated projects and for vector art, I use this instead</li>
</ul>
</li>
<li>PCSX2 PlayStation 2 emulator
<ul>
<li>Shoutout to this amazing emulator I use it to play Ratchet & Clank without needing my PS2</li>
<li>This emulator got really good in recent years and nowadays runs even difficult-to-emulate games (such as Ratchet & Clank) perfectly even on my laptop</li>
</ul>
</li>
</ul>
</div>
</div>
<p>Last updated: 2025-12-21</p>
</Content>
<style>
.container {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.column {
margin: 0 16px;
flex-basis: 50%;
}
h2 {
font-size: 1.4rem;
}
li {
display: block;
}
li li {
font-size: 0.9rem;
margin-left: 16px;
line-height: 1.2rem;
margin-top: 0px;
margin-bottom: 0px;
}
a {
color: var(--color-text);
}
@media screen and (max-width: 800px) {
.container {
flex-direction: column;
}
.column {
margin: 0 0 16px;
}
}
</style>

View File

@@ -19,7 +19,7 @@
description: "Small stories about my past music", description: "Small stories about my past music",
img: "/main/hypertext.webp", img: "/main/hypertext.webp",
altText: "", altText: "",
link: "my-tracks", link: "/art/music",
}, },
]; ];
</script> </script>
@@ -104,21 +104,20 @@
.project-banner-container { .project-banner-container {
position: relative; position: relative;
width: fit-content; width: 100%;
margin-left: auto;
margin-right: auto;
} }
.project-banner { .project-banner {
margin: 0; /* reset left/right margins */ margin: 0; /* reset left/right margins */
width: 100%; width: 100%;
object-fit: cover;
max-height: 300px; max-height: 300px;
} }
.project-icon { .project-icon {
float: left; float: left;
margin: 16px 16px 16px 0; margin: 16px 16px 16px 0;
width: 20%; width: 19%;
} }
.project-date-embed { .project-date-embed {

View File

@@ -29,19 +29,20 @@
<svelte:head> <svelte:head>
<title>Project N5 | denizk0461</title> <title>Homesick | denizk0461</title>
</svelte:head> </svelte:head>
<Content> <Content>
<Banner2 <Banner2
title="Project N5" title="Homesick"
banner="/projects/projectn5/banner2.webp" /> banner="/projects/projectn5/banner2.webp"
bannerAlt="The protagonist Laura standing on a floating platform in the purple test level. Ziplines are all around her and the text 'When this text is spinning, the game is not paused' is frozen in the sky." />
<p>I am currently working on a game under the working title <b>Project N5</b>! I'm aiming for it to be an action-adventure jump-and-run game inspired by games such as Ratchet & Clank. Development started on <b>2023-09-16</b> and rebooted on <b>2025-05-16</b>.</p> <p>I am currently working on a game under the working title <b>Homesick</b> (fka Project N5)! I'm aiming for it to be an action-adventure jump-and-run game inspired by games such as Ratchet & Clank. Development started on <b>2023-09-16</b> and rebooted on <b>2025-05-16</b>.</p>
<h2 id="devlog">Development Log</h2> <h2 id="devlog">Development Log</h2>
<p>Development log entries in reverse chronological order (newest to oldest).</p> <p>Development log entries in reverse chronological order (newest to oldest).</p>
<Gallery {entries} reverseTextOrder /> <Gallery {entries} />
</Content> </Content>

View File

@@ -4,19 +4,19 @@
let builds: LinkEntry[] = [ let builds: LinkEntry[] = [
{ {
text: "2023-10-07 (Protagonist #1)", text: "2023-10-07 (Protagonist #1)",
link: "https://files.denizk0461.dev/projectn5/2023-10-07.zip", link: "https://files.natconf.dev/public/projectn5/2023-10-07.zip",
}, },
{ {
text: "2023-12-23 (Protagonist #2)", text: "2023-12-23 (Protagonist #2)",
link: "https://files.denizk0461.dev/projectn5/2023-12-23.zip", link: "https://files.natconf.dev/public/projectn5/2023-12-23.zip",
}, },
{ {
text: "2024-03-25 (Protagonist #3 with jump animations)", text: "2024-03-25 (Protagonist #3 with jump animations)",
link: "https://files.denizk0461.dev/projectn5/2024-03-25.zip", link: "https://files.natconf.dev/public/projectn5/2024-03-25.zip",
}, },
{ {
text: "2025-08-16 (Laura era) [same build as the web version]", text: "2025-08-16 (Laura era) [same build as the web version]",
link: "https://files.denizk0461.dev/projectn5/2025-08-16.zip", link: "https://files.natconf.dev/public/projectn5/2025-08-16.zip",
}, },
]; ];
</script> </script>
@@ -33,9 +33,9 @@ The good news is that I'm done with all of this! The bachelor's thesis in partic
I decided to upload some playable builds of **Project N5**! Now that the website is running on my own server instead of being hosted by GitHub or Codeberg, I have a lot more freedom here. I decided to upload some playable builds of **Project N5**! Now that the website is running on my own server instead of being hosted by GitHub or Codeberg, I have a lot more freedom here.
The game, in its state from 2025-05-16 (before the reboot), is available to play in-browser [right here!](https://apps.denizk0461.dev/projectn5) It's not a terribly great experience, though. Loading times are significantly longer and shader compilation regularly freezes the game for longer than in a locally-saved copy. Some shaders are also not functioning as intended, though this only has a minor visual impact. The game was never optimised to work on the web, after all. The game, in its state from 2025-05-16 (before the reboot), is available to play in-browser [right here!](https://apps.natconf.dev/projectn5) It's not a terribly great experience, though. Loading times are significantly longer and shader compilation regularly freezes the game for longer than in a locally-saved copy. Some shaders are also not functioning as intended, though this only has a minor visual impact. The game was never optimised to work on the web, after all.
I've also uploaded old builds of the game [here](https://files.denizk0461.dev/projectn5). You'll find the following builds, one for each protagonist: I've also uploaded old builds of the game [here](https://files.natconf.dev/public/projectn5). You'll find the following builds, one for each protagonist:
<LinkList entries={builds} /> <LinkList entries={builds} />

View File

@@ -0,0 +1,75 @@
<script lang="ts">
import SubtitledImage from "$lib/components/subtitled-image.svelte";
</script>
Been a while. Again.
<SubtitledImage
image="habitkit.webp"
altText="A screenshot of the app HabitKit zoomed in on an entry labelled 'Project Laura' and subtitled '2025 IS THE YEAR OF LAURA'. On the left half are red dots scattered, marking days of progress, whereas the right half is entirely blank."
subtitle="aaaaaaaaaaaaaa"
alignment="right" />
## What's Been Going On
Not a lot. Well actually, that's not true. I returned to Germany after a four-month abroad semester in the UK. I deep-cleaned my apartment. I switched to Linux. I worked on my website a bunch. I [learned to draw](/blog/2026/0205). But there's not been any hard progress on Project N5.
I use this habit tracking app, [HabitKit](https://www.habitkit.app/), and it (unfortunately) quite clearly displays the lack of work on the game. But this doesn't mean I've given up on the game. Maybe I'm just saying this because I'm too stubborn though.
## What I've Realised
<SubtitledImage
image="papertracker.webp"
altText="A calendar with the title 'HOMESICK Abroad Progress Calendar'. Close to half the days are marked with a cross, with no discernible pattern visible."
subtitle="proof of progress"
alignment="left"
smaller />
In and especially after my drawing challenge, I've realised something I wasn't 100% sure applied across the board for me: I struggle to get started even on the things I enjoy most. I knew before that I have a hard time starting work on the game but enjoying it once I do begin, but I found that the very same applies to drawing.
There's just something inside of me that hinders me from doing what I love.
I tried combating this when I lived in the UK; I had a physical version of the habit tracker printed out (on uni credit) that served as a looming reminder day and night. The fact I needed this to begin with is ridiculous, I gotta say. But it did help, in a way, I think? Judging by the way my progress looks on the tracker app, there was at least a small increase in productive days.
## What's There to Look Forward To
Something I was really looking forward to was [Godot 4.6](https://godotengine.org/releases/4.6/), which finally released a while back!
The change that'll probably impact me the most is the (re-)introduction of inverse kinematics! That's something I've been longing to be able to implement for quite a while. But there are more cool changes that'll have a more minor but still interesting impact, like those on tonemapping, glow, LOD generation, and also the added support for controller lights and the possibility to use the DualSense's adaptive triggers in a future update.
I've also been keeping my eyes open for resources, and I found a couple that piqued my interest; namely, two shaders, a [toon shader by Binbun](https://binbun3d.itch.io/godot-ultimate-toon-shader) that seems more customisable and a lot more visually interesting than the one I've been using so far, and [Sky3D by TokisanGames](https://github.com/TokisanGames/Sky3D) for very pretty skies.
I am also planning on potentially looking into root motion for animation, once I get to that. It feels like a larger undertaking, and I may not end up pursuing that after all, but it seems like a cool way of getting better and more fluid-feeling motion out of a character.
## What's Changing
Possibly the biggest change I can announce right now is the name of the project. I've been calling it by this name for a while, but to make it somewhat official: the game is now called ***Homesick***.
The old 'Project N5' hasn't felt right for a long time. It's a relic of a time when the game was meant to be more of a shooter, Ratchet: Gladiator-style, with a robot protagonist fighting other robots while travelling across different planets. This isn't what I had in mind for the project for a very long time now. ***Homesick*** is meant to represent Laura's feelings, Laura's struggles when she learns what has happened to the world she once knew.
I'll probably change the URL structure soon to read `/projects/homesick`, but I'll set up redirects so old URLs won't be dead ends.
## What I've Done
Even in my state of absence from the project itself, I kept it in mind I never forgot about it! Still, the actual progress I made is rather minor. I did implement metro stations back in December, which allow the player to travel between levels and even to set locations within a level. Aside from that, most other changes are pretty basic and not really worth mentioning, like tweaking parameters or simplifying code blocks.
Possibly the most significant progress I made was on starting to model Laura v3. Wait, what?
## What I'm Planning to Do
Something I started but made very little progress with is Laura's redesign. The *next* redesign. I think I learned a lot from the v2 model (or v4 model, as I had called it before), but there's more to improve mesh-wise. However, I also want to make bigger stylistic changes. I feel that a lot of what I criticised about Laura v1 still applies in some way: she just doesn't look that interesting as a character. There's so much more I can do with her, I think, and the v2 model is just iteration on the v1 design. I made a low-poly mockup back in December to dump my brain onto the 3D canvas:
<SubtitledImage
image="laura3-preview.webp"
altText="A low-poly model of a human with red hair in a ponytail wearing a black mask, dark grey shirt with long sleeves, green trousers, and white shoes."
subtitle="an idea for where laura may be headed" />
Some pretty drastic colour palette changes. Red top, brown hair, and black/white everything else is just *boring*. That's like the look I wear irl except red instead of green. I wanted to mix up things.
I also believe that my recent efforts in learning to draw gave me a better perspective at modelling human bodies. There are details I had to pay attention to that I think just aren't reflected very well in the current model. It's small things like not making her arms and legs straight as matches. Or the proportions. Or her face; I think I'll be able to improve her eyes in particular. I struggled with that on v2, but ESPECIALLY on v1. And maybe I'll do a little better at drawing textures too, who knows?
It feels weird to design Laura for a *third* time now, but I guess this would be no different in an actual game studio's production. There's no way you'll land on the perfect character on your first try, right? It's just that a game studio may do this in a week or two, and I'm much slower at this.
Something else I need to revisit is the amalgamation I built a while back to manage Laura's different movement states. I think the way I built it is somehow both completely overengineered and also insufficient for use as a foundation for Laura's movement. It works *now*, don't get me wrong, but it's so bad to maintain. I'm not even gonna show it lol
Something I also *finally* need to do is just sit down and write story bits. It's something I've just not done (enough), and the story obviously isn't gonna magically appear out of thin air. There are ideas floating around in my head and I need to capture, connect, and build upon them. It's just something I've not done much before, so I'm worried and I keep postponing it, but just like with all other hobbies of mine: *I just need to get started!*

View File

@@ -16,7 +16,7 @@
<Banner2 <Banner2
title="{data.title}" title="{data.title}"
subtitle="Project N5 Devlog" subtitle="Homesick Devlog"
date="{data.date}" date="{data.date}"
banner="preview.webp" banner="preview.webp"
bannerAlt="{data.bannerAlt}" bannerAlt="{data.bannerAlt}"

View File

@@ -4,7 +4,7 @@ import { posts } from "../posts";
const xml = () => `<rss version="2.0"> const xml = () => `<rss version="2.0">
<channel> <channel>
<title>Project N5 Devlog</title> <title>Project N5 Devlog</title>
<link>https://denizk0461.dev/projects/projectn5/devlog/</link> <link>https://natconf.dev/projects/projectn5/devlog/</link>
<description><![CDATA[Development log for the game Project N5 by denizk0461]]></description>${getEntries()} <description><![CDATA[Development log for the game Project N5 by denizk0461]]></description>${getEntries()}
</channel> </channel>
</rss>`; </rss>`;
@@ -16,8 +16,8 @@ function getEntries(): String {
<item> <item>
<title><![CDATA[${entry.post.title}]]></title> <title><![CDATA[${entry.post.title}]]></title>
<description><![CDATA[${entry.post.description}]]></description> <description><![CDATA[${entry.post.description}]]></description>
<link>https://denizk0461.dev/projects/projectn5/devlog/${entry.key}</link> <link>https://natconf.dev/projects/projectn5/devlog/${entry.key}</link>
<guid isPermaLink="true">https://denizk0461.dev/projects/projectn5/devlog/${entry.key}</guid> <guid isPermaLink="true">https://natconf.dev/projects/projectn5/devlog/${entry.key}</guid>
<pubDate><![CDATA[${new Date(entry.post.date).toUTCString()}]]></pubDate> <pubDate><![CDATA[${new Date(entry.post.date).toUTCString()}]]></pubDate>
</item>`) </item>`)
entries.forEach(entry => { entries.forEach(entry => {

View File

@@ -11,6 +11,15 @@ export interface DevlogPostLink {
} }
export const posts: DevlogPostLink[] = [ export const posts: DevlogPostLink[] = [
{
key: "2026/0309",
post: {
title: "The Overdue Update",
date: "2026-03-09",
bannerAlt: "GDScript code zoomed in on a function called '_take_damage()' that checks whether the player has died and then reads: 'if is_dead: _die()'.",
description: "An excuse, a fresh character, and a new name.",
}
},
{ {
key: "2025/1207", key: "2025/1207",
post: { post: {

View File

@@ -65,10 +65,10 @@ export const games: Project[] = [
banner: "/projects/projectn5/banner2.webp", banner: "/projects/projectn5/banner2.webp",
icon: "", icon: "",
date: "September 2023 now", date: "September 2023 now",
title: "Project N5", title: "Homesick",
subtitle: "", subtitle: "",
paragraphs: [ paragraphs: [
"I'm currently working on a game developed using Godot, entitled Project N5! It's aiming to be an action-adventure 3D jump & run heavily inspired by games such as <a href='https://en.wikipedia.org/wiki/Ratchet_%26_Clank'>Ratchet & Clank</a>.", "I'm currently working on a game developed using Godot, entitled Homesick! It's aiming to be an action-adventure 3D jump & run heavily inspired by games such as <a href='https://en.wikipedia.org/wiki/Ratchet_%26_Clank'>Ratchet & Clank</a>.",
"I maintain a development log, feel free to check it out if you're curious! Or play some of the old builds available for download below.", "I maintain a development log, feel free to check it out if you're curious! Or play some of the old builds available for download below.",
], ],
links: [ links: [
@@ -77,12 +77,12 @@ export const games: Project[] = [
link: "/projects/projectn5", link: "/projects/projectn5",
}, },
{ {
text: "Play an <b>old build</b> (developed until 2025-05-16)", text: "Play an <b>old web build</b> (developed until 2025-05-16)",
link: "https://apps.denizk0461.dev/projectn5", link: "https://apps.natconf.dev/projectn5",
}, },
{ {
text: "Download the <b>old Windows builds</b>", text: "Download the <b>old Windows builds</b>",
link: "https://files.denizk0461.dev/projectn5", link: "https://files.natconf.dev/public/projectn5",
}, },
], ],
status: ProjectStatus.ACTIVE, status: ProjectStatus.ACTIVE,
@@ -102,7 +102,7 @@ export const games: Project[] = [
links: [ links: [
{ {
text: "View the latest <b>Magician</b> build", text: "View the latest <b>Magician</b> build",
link: "https://apps.denizk0461.dev/magician", link: "https://apps.natconf.dev/magician",
}, },
], ],
status: ProjectStatus.ABANDONED, status: ProjectStatus.ABANDONED,
@@ -139,7 +139,7 @@ export const games: Project[] = [
links: [ links: [
{ {
text: "Play <b>Swords & Stuff</b>", text: "Play <b>Swords & Stuff</b>",
link: "https://apps.denizk0461.dev/swordsnstuff", link: "https://apps.natconf.dev/swordsnstuff",
}, },
], ],
status: ProjectStatus.ABANDONED, status: ProjectStatus.ABANDONED,
@@ -159,11 +159,11 @@ export const games: Project[] = [
links: [ links: [
{ {
text: "Play <b>TADS 1</b>", text: "Play <b>TADS 1</b>",
link: "https://apps.denizk0461.dev/tads/1", link: "https://apps.natconf.dev/tads/1",
}, },
{ {
text: "Play <b>TADS 2</b>", text: "Play <b>TADS 2</b>",
link: "https://apps.denizk0461.dev/tads/2", link: "https://apps.natconf.dev/tads/2",
}, },
], ],
status: ProjectStatus.FINISHED, status: ProjectStatus.FINISHED,
@@ -189,7 +189,7 @@ export const hardware: Project[] = [
}, },
{ {
text: "Get the <b>PCB and STL files</b>", text: "Get the <b>PCB and STL files</b>",
link: "https://files.denizk0461.dev/daisyfm/", link: "https://files.natconf.dev/public/daisyfm/",
}, },
{ {
text: "View the code files on <b>Codeberg</b>", text: "View the code files on <b>Codeberg</b>",
@@ -317,7 +317,7 @@ export const music: Project[] = [
links: [ links: [
{ {
text: "Listen & download on my <b>copyparty</b> instance", text: "Listen & download on my <b>copyparty</b> instance",
link: "https://files.denizk0461.dev/my_tracks/Dreamworld/", link: "https://files.natconf.dev/public/my_tracks/Dreamworld/",
}, },
], ],
status: ProjectStatus.FINISHED, status: ProjectStatus.FINISHED,
@@ -337,7 +337,7 @@ export const music: Project[] = [
links: [ links: [
{ {
text: "Listen & download on my <b>copyparty</b> instance", text: "Listen & download on my <b>copyparty</b> instance",
link: "https://files.denizk0461.dev/my_tracks/A%20New%20Beginning/", link: "https://files.natconf.dev/public/my_tracks/A%20New%20Beginning/",
}, },
], ],
status: ProjectStatus.FINISHED, status: ProjectStatus.FINISHED,

View File

@@ -1,41 +0,0 @@
<script>
import SubtitledImage from "$lib/components/subtitled-image.svelte";
import Content from "$lib/viewport/content.svelte";
</script>
<Content>
<p>this is a test page. please disregard. or not i'm not your boss</p>
<SubtitledImage
image="/meta/about/taskbar.webp"
altText="taskbar"
subtitle="My taskbar with a little Clank backback on the left side because it's very cute. very cute. very very very cute indeed. very much indeed so. sample text. lorem ipsum. this text needs to break. how long can i make this text be? i need to write a lot to see how this text box handles very long text and if this actually looks good. let's see hmm what can I think of? nothing really. PUSH UR T3MPRR"
/>
<SubtitledImage
image="/projects/projectn5/devlog/2025/0427/laura-v4-full.webp"
altText="taskbar"
subtitle="old as fuck laura. very tall. very very very tall indeed. very much indeed so. sample text. lorem ipsum. PUSH UR T3MPRR"
alignment="left"
/>
<p>test text to see whether this aligmne tactually works afdsdfasjd iofjaopsid iofjasiop[d iofajiope juiorpfjah sidfokljasolkdjf ioajsu0oid kfjpzxj niujkhjozx jcio gjzoxij cviozjhxioc jvzxjc asdjfoasd iozxoic uoizxcu hoiwe oijfio aswjiofjo sajdj asd hnnhnhnhan han n nhnhnhnhnnhnh nhnh nggnhngh ng nhng nhng nh na asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdftest text to see whether this aligmne tactually works afdsdfasjd iofjaopsid iofjasiop[d iofajiope juiorpfjah sidfokljasolkdjf ioajsu0oid kfjpzxj niujkhjozx jcio gjzoxij cviozjhxioc jvzxjc asdjfoasd iozxoic uoizxcu hoiwe oijfio aswjiofjo sajdj asd hnnhnhnhan han n nhnhnhnhnnhnh nhnh nggnhngh ng nhng nhng nh na asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdftest text to see whether this aligmne tactually works nhng nh na asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdftest text to see whether this aligmne tactually works afdsdfasjd if asdf</p>
<SubtitledImage
image="/meta/about/taskbar.webp"
altText="taskbar"
subtitle="My taskbar with a little Clank backback on the left side because it's very cute. very cute. very very very cute indeed. very much indeed so. sample text. lorem ipsum. this text needs to break. how long can i make this text be? i need to write a lot to see how this text box handles very long text and if this actually looks good. let's see hmm what can I think of? nothing really. PUSH UR T3MPRR"
alignment="right"
/>
<p>test text to see whether this aligmne tactually works afdsdfasjd iofjaopsid iofjasiop[d iofajiope juiorpfjah sidfokljasolkdjf ioajsu0oid kfjpzxj niujkhjozx jcio gjzoxij cviozjhxioc jvzxjc asdjfoasd iozxoic uoizxcu hoiwe oijfio aswjiofjo sajdj asd hnnhnhnhan han n nhnhnhnhnnhnh nhnh nggnhngh ng nhng nhng nh na asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdftest text to see whether this aligmne tactually works afdsdfasjd iofjaopsid iofjasiop[d iofajiope juiorpfjah sidfokljasolkdjf ioajsu0oid kfjpzxj niujkhjozx jcio gjzoxij cviozjhxioc jvzxjc asdjfoasd iozxoic uoizxcu hoiwe oijfio aswjiofjo sajdj asd hnnhnhnhan han n nhnhnhnhnnhnh nhnh nggnhngh ng nhng nhng nh na asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdftest text to see whether this aligmne tactually works afdsdfasjd iofjaopsid iofjasiop[d iofajiope juiorpfjah sidfokljasolkdjf ioajsu0oid kfjpzxj niujkhjozx jcio gjzoxij cviozjhxioc jvzxjc asdjfoasd iozxoic uoizxcu hoiwe oijfio aswjiofjo sajdj asd hnnhnhnhan han n nhnhnhnhnnhnh nhnh nggnhngh ng nhng nhng nh na asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdftest text to see whether this aligmne tactually works afdsdfasjd iofjaopsid iofjasiop[d iofajiope juiorpfjah sidfokljasolkdjf ioajsu0oid kfjpzxj niujkhjozx jcio gjzoxij cviozjhxioc jvzxjc asdjfoasd iozxoic uoizxcu hoiwe oijfio aswjiofjo sajdj asd hnnhnhnhan han n nhnhnhnhnnhnh nhnh nggnhngh ng nhng nhng nh na asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdftest text to see whether this aligmne tactually works afdsdfasjd iofjaopsid iofjasiop[d iofajiope juiorpfjah sidfokljasolkdjf ioajsu0oid kfjpzxj niujkhjozx jcio gjzoxij cviozjhxioc jvzxjc asdjfoasd iozxoic uoizxcu hoiwe oijfio aswjiofjo sajdj asd hnnhnhnhan han n nhnhnhnhnnhnh nhnh nggnhngh ng nhng nhng nh na asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf asdf</p>
<SubtitledImage
image="/meta/about/taskbar.webp"
altText="taskbar"
alignment="right"
/>
<SubtitledImage
image="/meta/about/taskbar.webp"
altText="taskbar"
/>
</Content>

BIN
static/art/banner.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 932 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 259 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 446 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 392 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 698 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 223 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 223 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 819 B

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 529 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB