Compare commits

..

27 Commits

Author SHA1 Message Date
a8f012066c removed horizontally-centre-aligned class to be superseded by ImageRow component 2026-05-05 13:16:54 +02:00
165d0b7042 ContentSidebar now uses grid; adjusted sizing 2026-04-27 15:05:31 +02:00
cf125809bd improved responsiveness of ContentSidebar and TableOfContents when resizing window 2026-04-27 14:49:48 +02:00
72347769c3 improved styling of side-bound TableOfContents component 2026-04-26 22:39:07 +02:00
a1effdec8e fixed ContentSidebar sizing and margin of first item 2026-04-26 22:34:02 +02:00
3586d7eece replaced manually-added table of contents with automatic ContentSidebar toc where needed 2026-04-26 22:22:29 +02:00
b24712ef4c banner now controlled by Content and ContentSidebar components 2026-04-26 22:13:15 +02:00
feebf17bd8 removed blur effect from projects page to counteract performance drop on that page 2026-04-26 21:17:36 +02:00
5edd4c7a6d improved interactivity of drawing gallery page 2026-04-26 21:08:16 +02:00
9ff26d3c0b swapped copyparty for drawing gallery link on main page; display latest drawing date there too; added drawing gallery link to header and footer 2026-04-26 12:30:15 +02:00
1cc7323c36 main page can now fetch blog post updated dates 2026-04-26 12:19:43 +02:00
331b8dd95b ImageRow now deprioritises unhovered cards only when container is hovered 2026-04-26 12:15:28 +02:00
1348be3ee9 fixed alt text on feed banner 2026-04-26 10:47:45 +02:00
d746f8ef6f componised ImageRow from /feed 2026-04-26 10:46:56 +02:00
455eb4c291 added outline to drawing gallery on hover 2026-04-26 10:16:27 +02:00
ac123ad0d7 updated LinkRow styling 2026-04-25 12:24:53 +02:00
3a9d4dd2fc moved feed post metadata out of individual posts to be more easily accessible 2026-04-25 12:14:14 +02:00
83c8cdaa34 removed drawings from projects and feed pages 2026-04-24 22:37:28 +02:00
6ab1b392de changed redirect of art/drawings 2026-04-24 22:31:12 +02:00
6578c74b60 added drawings to drawing gallery 2026-04-24 22:30:28 +02:00
353a3a1846 added /drawings 2026-04-24 21:30:33 +02:00
7e78be19da updated gallery description texts for deej0461 2026-04-24 21:02:28 +02:00
dc9907f264 hid 88x31 buttons for the time being 2026-04-24 20:51:25 +02:00
df1a4c8db4 added fancy marker for latest update date in main page link row 2026-04-24 20:48:22 +02:00
db236538a0 added date of latest feed post to main page 2026-04-24 20:33:30 +02:00
f2476dbe8a added alt texts to feed post 2026/0410 2026-04-24 20:09:50 +02:00
97b29f8dce added new project to feed 2026-04-24 20:08:21 +02:00
63 changed files with 1147 additions and 557 deletions

View File

@@ -1,57 +1,43 @@
<script lang="ts">
import { type BannerContent } from './components/banner-content';
let {
title,
date = "", // date posted
dateUpdated = "",
dateIndeterminate = "", // raw date without an explanation marker next to it
subtitle = "",
banner = "",
bannerAlt = "",
tags = [],
pixelated,
content,
}: {
title: string;
date?: string;
dateUpdated?: string;
dateIndeterminate?: string;
subtitle?: string;
banner?: string;
bannerAlt?: string;
tags?: string[];
pixelated?: boolean;
content: BannerContent;
} = $props();
</script>
{#snippet titles({title, subtitle, date}: {title: string, subtitle: string, date: string})}
{#snippet titles()}
<div class="title-container">
<div class="title-text-container">
<h1 class="title">{title}</h1>
<h1 class="title">{content.title}</h1>
{#if subtitle}
<p class="subtitle">[ {subtitle} ]</p>
{#if content.subtitle}
<p class="subtitle">[ {content.subtitle} ]</p>
{/if}
{#if tags.length}
{#if content.tags && content.tags.length > 0}
<div class="tag-container">
{#each tags as tag}
{#each content.tags as tag}
<span class="post-tag">{tag}</span>
{/each}
</div>
{/if}
</div>
{#if date || dateUpdated || dateIndeterminate}
{#if content.date || content.dateUpdated || content.dateIndeterminate}
<div class="date-container">
{#if dateIndeterminate}
<p class="date">:: {dateIndeterminate}</p>
{#if content.dateIndeterminate && content.dateIndeterminate != "undefined"}
<p class="date">:: {content.dateIndeterminate}</p>
{/if}
{#if date}
<p class="date">posted :: {date}</p>
{#if content.date && content.date != "undefined"}
<p class="date">posted :: {content.date}</p>
{/if}
{#if dateUpdated}
<p class="date">last updated :: {dateUpdated}</p>
{#if content.dateUpdated && content.dateUpdated != "undefined"}
<p class="date">last updated :: {content.dateUpdated}</p>
{/if}
</div>
{/if}
@@ -59,13 +45,13 @@
{/snippet}
<div class="container">
{#if banner && banner !== ""}
<a class="banner-container" href={banner}>
<img class="banner {pixelated ? "pixelated-img" : ""}" src={banner} alt={bannerAlt}>
{#if content.banner && content.banner !== ""}
<a class="banner-container" href={content.banner}>
<img class="banner {content.pixelated ? "pixelated-img" : ""}" src={content.banner} alt={content.bannerAlt}>
</a>
{/if}
{@render titles({title, subtitle, date})}
{@render titles()}
<hr>
</div>

View File

@@ -0,0 +1,11 @@
export interface BannerContent {
title: string;
date?: string; // date posted
dateUpdated?: string;
dateIndeterminate?: string; // raw date without an explanation marker next to it
subtitle?: string;
banner?: string;
bannerAlt?: string;
tags?: string[];
pixelated?: boolean;
}

View File

@@ -1,5 +1,12 @@
<script lang="ts">
import {onMount} from 'svelte';
import { onMount } from 'svelte';
let {
type,
}: {
// possible values: none, 'side'
type: string;
} = $props();
interface TocEntry {
text: string;
@@ -63,7 +70,9 @@
{/snippet}
{#if tocEntries.length > 0}
<div class="toc-container blurred-background">
<div class="{type ? "toc-container-side" : "toc-container"} blurred-background">
<p class="toc-header">on this page</p>
<ul class="toc-list">
{#each tocEntries as entry}
{@render tocEntryLine({ entry })}
@@ -74,21 +83,39 @@
<style>
:global {
body {
--padding-indent-base: 44px;
--padding-level-indent: 24px;
.toc-container, .toc-container-side {
box-sizing: border-box;
padding: 16px 0;
border-radius: var(--border-radius);
}
.toc-container {
--padding-indent-base: 44px;
--padding-level-indent: 24px;
max-width: var(--width-toc);
margin-left: auto;
margin-right: auto;
margin-top: 12px;
box-sizing: border-box;
background-color: var(--color-background-highlight);
padding: 16px 0;
border: var(--border-style) var(--border-dash-size) var(--color-highlight);
border-radius: var(--border-radius);
background-color: var(--color-background-highlight);
}
.toc-container-side {
--padding-indent-base: 20px;
--padding-level-indent: var(--padding-indent-base);
/* width: 300px; */
margin-left: 12px;
margin-right: 12px;
position: sticky;
top: 64px;
border-left: var(--border-style) var(--border-dash-size) var(--color-highlight);
}
.toc-container-side .toc-list a {
border-top-right-radius: var(--border-radius);
border-bottom-right-radius: var(--border-radius);
}
.toc-list {
@@ -97,6 +124,14 @@
width: 100%;
}
.toc-header {
margin: 0 var(--padding-indent-base);
font-weight: 700;
font-style: italic;
color: var(--color-highlight);
font-family: var(--font-mono);
}
.toc-list a {
width: 100%;
font-size: 1.0rem;

View File

@@ -1,8 +1,9 @@
<script lang="ts">
export interface GalleryRowEntry {
export interface LinkRowEntry {
title: string;
description: string;
img: string;
latestUpdate?: string;
altText: string;
link: string;
}
@@ -10,7 +11,7 @@
let {
entries,
}: {
entries: GalleryRowEntry[];
entries: LinkRowEntry[];
} = $props();
</script>
@@ -23,6 +24,9 @@
<div class="row-text-container">
<p class="row-title">&gt; {entry.title}</p>
<p class="row-description">{@html entry.description}</p>
{#if entry.latestUpdate}
<p class="row-updated">updated: <span class="row-updated-date">{entry.latestUpdate}</span></p>
{/if}
</div>
</a>
{/each}
@@ -78,6 +82,7 @@
.row-text-container {
margin-top: 8px;
margin-bottom: 4px;
}
.row-title {
@@ -90,7 +95,38 @@
.row-description {
font-size: 1.0rem;
line-height: 1.3rem;
margin: 0 0 4px 0;
margin: 0;
}
.row-updated, .row-updated-date {
font-size: 0.9rem;
line-height: 1.2rem;
margin: 0;
font-style: italic;
width: fit-content;
transition: color var(--duration-animation) var(--anim-curve),
background-color var(--duration-animation) var(--anim-curve);
}
.row-updated {
margin-top: 4px;
padding: 2px 8px;
font-weight: 500;
background-color: var(--color-background-highlight);
border-radius: var(--border-radius);
}
.row-entry:hover .row-updated {
color: var(--color-text-dark);
background-color: var(--color-highlight);
}
.row-updated-date {
font-weight: 600;
}
.row-entry:hover .row-updated-date {
color: var(--color-text-dark);
}
@media screen and (max-width: 600px) {

View File

@@ -0,0 +1,4 @@
export interface A11yImage {
src: string;
alt: string;
}

View File

@@ -0,0 +1,75 @@
<script lang="ts">
import { type A11yImage } from "./a11y-img";
let {
images,
// path in which all images lie. this must be the same for all images. if left blank, "./" is used instead
path,
}: {
images: A11yImage[];
path?: string;
} = $props();
</script>
<div class="image-gallery">
{#each images as i}
<a class="image-gallery-link" href="{path ?? "./"}/{i.src}">
<img class="image-gallery-img" loading="lazy" src="{path ?? "./"}/{i.src}" alt={i.alt}>
</a>
{/each}
</div>
<style>
.image-gallery {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 4px;
}
.image-gallery-img {
width: 100%;
height: 100%;
display: block;
object-fit: cover;
filter: brightness(100%) saturate(80%);
max-height: 320px;
transition: filter var(--duration-animation) var(--anim-curve),
scale var(--duration-animation) var(--anim-curve);
scale: 1.06;
}
.image-gallery-link {
outline: var(--border-style) var(--border-dash-size) transparent;
border-radius: var(--border-radius);
z-index: 10;
transition: outline-color var(--duration-animation) var(--anim-curve);
overflow: hidden;
margin: 0;
}
.image-gallery:hover .image-gallery-img {
filter: brightness(60%) saturate(80%);
}
.image-gallery-link:hover .image-gallery-img {
filter: brightness(100%) saturate(100%);
scale: 1.0;
}
.image-gallery-link:hover {
outline-color: var(--color-highlight);
}
@media screen and (max-width: 800px) {
.image-gallery-link:hover {
outline-color: transparent;
}
.image-gallery-img {
border-radius: 0;
scale: 1.0;
filter: brightness(100%) saturate(100%);
}
}
</style>

View File

@@ -1,33 +1,78 @@
<div class="content">
<div class="side">
<slot name="side-left" />
</div>
<div class="main">
<slot name="main" />
<script lang="ts">
import Banner2 from "$lib/banner2.svelte";
import type { BannerContent } from "$lib/components/banner-content";
import ScrollTopButton from "$lib/components/scroll-top-button.svelte";
import TableOfContents from "$lib/components/table-of-contents.svelte";
import type { Snippet } from "svelte";
let {
children,
bannerContent,
}: {
children: Snippet,
bannerContent: BannerContent;
} = $props();
</script>
<div class="container">
<Banner2 content={bannerContent}/>
<div class="content">
<div class="content-sidebar-main-container">
{@render children()}
<ScrollTopButton />
</div>
<div class="side">
<TableOfContents type="side" />
</div>
</div>
</div>
<style>
.content {
max-width: 2000px;
margin: 0 auto;
display: flex;
flex-direction: row;
padding: 0 24px;
/* not elegant at all but it works */
:global {
.content-sidebar-main-container > *:nth-child(1) {
margin-top: 0 !important;
}
}
.container {
max-width: var(--page-width);
margin: 0 auto;
padding: 0 24px;
box-sizing: border-box;
}
.content {
max-width: var(--page-width);
width: 100%;
display: grid;
grid-template-columns: auto 300px;
}
.content-sidebar-main-container {
width: 100%;
}
.side {
min-width: 400px;
width: 100%;
margin-top: 32px;
margin-bottom: 8px;
}
@media screen and (max-width: 800px) {
.content {
padding: 0 8px;
flex-direction: column;
width: 100%;
grid-template-rows: auto auto;
grid-template-columns: 1fr;
}
.side {
min-width: 0;
max-width: 100%;
margin-top: 8px;
width: 100%;
order: -1;
}
}
</style>

View File

@@ -1,10 +1,23 @@
<script lang="ts">
import Banner2 from "$lib/banner2.svelte";
import type { BannerContent } from "$lib/components/banner-content";
import ScrollTopButton from "$lib/components/scroll-top-button.svelte";
import type { Snippet } from "svelte";
let { children } = $props();
let {
children,
bannerContent,
}: {
children: Snippet,
bannerContent?: BannerContent;
} = $props();
</script>
<div class="main-content">
{#if bannerContent}
<Banner2 content={bannerContent} />
{/if}
<ScrollTopButton />
{@render children()}
</div>

View File

@@ -28,6 +28,7 @@
<a class="link-level-2" href="/projects/projectn5">Homesick</a>
<a href="/feed">Feed</a>
<a href="/blog">Blog</a>
<a href="/drawings">Drawing Gallery</a>
</div>
<div class="content-box">
<h6>Meta</h6>
@@ -128,6 +129,7 @@
.commit {
display: inline;
font-size: inherit;
}
@media screen and (max-width: 800px) {

View File

@@ -3,6 +3,7 @@
<a href="/projects">projects</a>
<a href="/feed">feed</a>
<a href="/blog">blog</a>
<a href="/drawings">drawings</a>
<a href="/meta/about">about</a>
{/snippet}

View File

@@ -323,15 +323,6 @@
font-family: var(--font-lightyears);
}
.horizontally-centre-aligned {
width: var(--media-width);
display: flex;
justify-content: center;
align-items: center;
margin-left: auto;
margin-right: auto;
}
.inline-img-left {
float: left;
max-width: 24%;

View File

@@ -1,15 +1,21 @@
<script lang="ts">
import Content from "$lib/viewport/content.svelte";
import GalleryRow, { type GalleryRowEntry } from "$lib/lists/gallery-row.svelte";
import LinkRow from "$lib/lists/link-row.svelte";
import { posts as devlogPosts } from "./projects/projectn5/devlog/posts";
import { posts as blogPosts } from "./blog/posts";
import IndieButton from "$lib/components/indie-button.svelte";
import { buttons } from "$lib/components/indie-button";
// import IndieButton from "$lib/components/indie-button.svelte";
// import { buttons } from "$lib/components/indie-button";
import { onMount } from "svelte";
import { getLatestPostDate } from "./feed/feed";
import { drawings } from "./drawings/drawings";
let latestDevlogDate = devlogPosts[0].post.date;
let latestBlogDate = blogPosts[0].post.date;
// this only fetches the updated date if the latest post has been updated
let latestBlogDate = blogPosts[0].post.dateUpdated ?? blogPosts[0].post.date;
let latestDrawingDate = drawings[drawings.length - 1].date;
let latestStatusContent = $state("fetching status...");
let latestStatusTimestamp = $state("?");
@@ -33,47 +39,6 @@
onMount(() => {
getLatestStatus();
})
const galleryTopRow: GalleryRowEntry[] = [
{
title: "Homesick devlog",
description: `my active Godot game project about finding yourself in an unfamiliar future. <i>latest update: ${latestDevlogDate}</i>`,
img: "projects/projectn5/banner2.webp",
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",
},
{
title: "Projects",
description: "<b>[updated]</b> an overview of all my projects!",
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.",
link: "projects",
},
];
const galleryBottomRow: GalleryRowEntry[] = [
{
title: "Creative Feed",
description: `the small things I make find a home here.`,
img: "feed/banner.webp",
altText: "A blue screen with the text 'how do you do art ? 1. face your fears 2. become your heroes'. The 'art' looks to have been edited in. The music artist Porter Robinson is standing in the bottom right corner.",
link: "feed",
},
{
title: "Blog",
description: `a place where I write about random things. <i>latest post: ${latestBlogDate}</i>`,
img: "blog/robert.webp",
altText: "View at a tram bridge rising and then curving to the left.",
link: "blog",
},
{
title: "Files",
description: "find things I've put for download on my copyparty instance.",
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 brandenbur ger tor!' is displayed.",
link: "https://files.natconf.dev/public/",
},
];
</script>
<svelte:head>
@@ -124,8 +89,59 @@
<hr>
<GalleryRow entries={galleryTopRow} />
<GalleryRow entries={galleryBottomRow} />
<LinkRow entries={
[
{
title: "Homesick devlog",
description: `my active Godot game project about finding yourself in an unfamiliar future.`,
latestUpdate: latestDevlogDate,
img: "projects/projectn5/banner2.webp",
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",
},
{
title: "Projects",
description: "<b>[updated]</b> an overview of all my projects!",
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.",
link: "projects",
},
]} />
<LinkRow entries={
[
{
title: "Creative Feed",
description: `the small things I make find a home here.`,
latestUpdate: getLatestPostDate(),
img: "feed/banner.webp",
altText: "A blue screen with the text 'how do you do art ? 1. face your fears 2. become your heroes'. The 'art' looks to have been edited in. The music artist Porter Robinson is standing in the bottom right corner.",
link: "feed",
},
{
title: "Blog",
description: `a place where I write about random things.`,
latestUpdate: latestBlogDate,
img: "blog/robert.webp",
altText: "View at a tram bridge rising and then curving to the left.",
link: "blog",
},
{
title: "Drawing Gallery",
description: "a collection of my digital and physical drawings.",
latestUpdate: latestDrawingDate,
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: "Files",
// description: "find things I've put for download on my copyparty instance.",
// 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 brandenbur ger tor!' is displayed.",
// link: "https://files.natconf.dev/public/",
// },
]} />
<hr>
@@ -158,13 +174,13 @@
<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 class="button-container">
<!-- <div class="button-container">
{#each buttons as button}
<IndieButton button={button} />
{/each}
</div>
<p>to be expanded!</p>
<p class="small-supertext">currently trying to come up with ideas for my own 88x31 button</p>
<p class="small-supertext">currently trying to come up with ideas for my own 88x31 button</p> -->
</div>
</Content>
@@ -175,7 +191,7 @@
gap: 16px;
}
.button-container {
/* .button-container {
flex: 2;
}
@@ -183,7 +199,7 @@
display: flex;
flex-wrap: wrap;
gap: 8px;
}
} */
.webring {
display: flex;

View File

@@ -1,5 +1,5 @@
import { redirect } from "@sveltejs/kit";
export function load() {
redirect(308, '/feed');
redirect(308, '/drawings');
}

View File

@@ -7,12 +7,12 @@
<title>My Tracks | denizk0461</title>
</svelte:head>
<Content>
<Banner2
title="My Tracks"
subtitle=""
banner=""
bannerAlt="" />
<Content bannerContent={{
title: "My Tracks",
subtitle: "",
banner: "",
bannerAlt: "",
}}>
well this is awkward. there's nothing here yet. come back later?

View File

@@ -24,11 +24,11 @@
<title>Blog | denizk0461</title>
</svelte:head>
<Content>
<Banner2
title="Blog"
banner="robert.webp"
bannerAlt="View at a tram bridge rising and then curving to the left." />
<Content bannerContent={{
title: "Blog",
banner: "robert.webp",
bannerAlt: "View at a tram bridge rising and then curving to the left.",
}}>
<!-- TODO descriptions on filter click -->
<div class="tag-filters-alt">

View File

@@ -1,7 +1,5 @@
<script>
import Banner2 from "$lib/banner2.svelte";
import Content from "$lib/viewport/content.svelte";
import TableOfContents from "$lib/components/table-of-contents.svelte";
import ContentSidebar from "$lib/viewport/content-sidebar.svelte";
export let data;
</script>
@@ -12,18 +10,16 @@
<meta name="DCTERMS.created" content="{data.date}T{data.time}">
</svelte:head>
<Content>
<Banner2
title="{data.title}"
subtitle="{data.subtitle}"
date="{data.date}"
dateUpdated="{data.dateUpdated}"
banner="{data.banner}"
bannerAlt="Banner for blog post '{data.title}'"
tags={data.tags} />
<TableOfContents />
<ContentSidebar bannerContent={{
title: data.title,
subtitle: data.subtitle,
date: data.date,
dateUpdated: data.dateUpdated,
banner: data.banner,
bannerAlt: `Banner for blog post '${data.title}'`,
tags: data.tags,
}}>
<svelte:component this={data.content} />
</Content>
</ContentSidebar>

View File

@@ -0,0 +1,151 @@
<script lang="ts">
import Content from "$lib/viewport/content.svelte";
import Banner2 from "$lib/banner2.svelte";
import { drawings } from "./drawings";
let selectedIndex: number = $state(0);
let drawingsRev = drawings.toReversed();
</script>
<svelte:head>
<title>Drawing Gallery | denizk0461</title>
</svelte:head>
<Content bannerContent={{
title: "Drawing Gallery",
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.",
subtitle: "sketches & stuff",
}}>
<div class="selected-container">
<a class="selected-img-link" href={drawingsRev[selectedIndex].src}>
<img
class="selected-img"
src={drawingsRev[selectedIndex].src}
alt={drawingsRev[selectedIndex].alt}
/>
</a>
<div class="selected-text-container">
<div class="selected-text-header">
{#if drawingsRev[selectedIndex].title}
<p class="selected-title">{drawingsRev[selectedIndex].title}</p>
{/if}
<p class="selected-date">from {drawingsRev[selectedIndex].date}</p>
</div>
{#each drawingsRev[selectedIndex].desc as d}
<p>{@html d}</p>
{/each}
</div>
</div>
<hr>
<div class="img-button-container">
{#each drawingsRev as d, index}
<button class="img-button" onclick={() => selectedIndex = index}>
<img src={d.src} alt={d.alt} loading="lazy" />
</button>
{/each}
</div>
</Content>
<style>
.selected-container {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
}
.selected-img-link {
width: fit-content;
height: 400px;
margin: 0 auto;
overflow: hidden;
}
.selected-img-link:hover .selected-img {
scale: 1.06;
}
.selected-img {
object-fit: contain;
width: 100%;
height: 100%;
max-height: initial;
scale: 1.0;
transition: scale var(--duration-animation) var(--anim-curve);
}
.selected-text-container {
display: flex;
flex-direction: column;
gap: 8px;
overflow: scroll;
padding-right: 12px;
max-height: var(--media-height);
}
.selected-text-header p {
font-weight: 600;
font-style: italic;
}
.selected-title {
font-size: 1.1rem;
}
.selected-date {
font-size: 1.0rem;
}
.selected-text-container * {
margin: 0;
}
.img-button-container {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
gap: 4px;
}
.img-button {
cursor: pointer;
width: 100%;
overflow: hidden;
border-radius: var(--border-radius);
/* filter: brightness(70%) saturate(60%); */
outline: var(--border-style) var(--border-dash-size) transparent;
transition: outline-color var(--duration-animation) var(--anim-curve);
}
.img-button img {
width: 100%;
height: 200px;
object-fit: cover;
filter: brightness(100%) saturate(80%);
transition: scale var(--duration-animation) var(--anim-curve),
filter var(--duration-animation) var(--anim-curve);
}
.img-button:hover img {
scale: 1.12;
}
.img-button:active img {
scale: 1.08;
}
.img-button-container:hover img {
filter: brightness(70%) saturate(60%);
}
.img-button:hover {
outline-color: var(--color-highlight);
}
.img-button:hover img {
filter: brightness(100%) saturate(100%);
}
</style>

View File

@@ -0,0 +1,171 @@
export interface Drawing {
title?: string;
date: string;
src: string;
alt: string;
desc: string[];
}
export let drawings: Drawing[] = [
{
date: "2026-01-29",
src: "/blog/2026/0129/girl.webp",
alt: "A small drawing of an anime-style girl's head. She has a ponytail and is looking towards the left with a concentrated gaze.",
desc: [
"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!",
],
},
{
title: "Bread Girl",
date: "2026-01-30",
src: "2026/breadgirl.webp",
alt: "An anime-style girl chewing on a piece of bread. She wears a ponytail and a sleeveless top.",
desc: [
"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.",
],
},
{
title: "Elizabeth",
date: "2026-02-18",
src: "/blog/2026/0205/13-2.webp",
alt: "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.",
desc: [
"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!",
],
},
{
title: "Emilia",
date: "2026-02-23",
src: "/blog/2026/0205/18.webp",
alt: "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.",
desc: [
"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.",
],
},
{
title: "Porter Robinson fanart",
date: "2026-02-26",
src: "/blog/2026/0205/21.webp",
alt: "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.",
desc: [
"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.",
],
},
{
title: "Cyborg Arm",
date: "2026-02-27",
src: "/blog/2026/0205/22.webp",
alt: "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.",
desc: [
"Possibly my favourite sketch from the drawing challenge, because she looks cool, but also because her design deviates from the other characters a bit.",
],
},
{
date: "2026-03-04",
src: "/blog/2026/0205/27-1.webp",
alt: "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.",
desc: [
"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!",
],
},
{
date: "2026-03-10",
src: "2026/0310.webp",
alt: "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.",
desc: [
"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.",
],
},
{
date: "2026-03-18",
src: "2026/0318.webp",
alt: "A smiling character with a ponytail and bangs. Their eyes are closed.",
desc: [
"I drew this character while testing brushes in Krita. It's why the coloured spaces all use a crayon-like brush whereas the shadows are dotted. It doesn't fit together, but hey, I had to learn that at some point, right?",
"I like her expression, it's cute.",
],
},
{
date: "2026-03-28",
src: "2026/0328.webp",
alt: "Different eyes in different colours. The eye in the centre has a four-pronged star for a pupil.",
desc: [
"Some eye drawing practice while I was staying at a friends' house. Wanted to try some shading and overall just making the eyes look more interesting.",
"In retrospect, the eyebrows don't look good, and the eyelashes (on the bottom eye) are way off. I do like the star for the pupil on the middle one though!",
],
},
{
date: "2026-03-29",
src: "2026/0329.webp",
alt: "Different eyes in different colours.",
desc: [
"More eye drawing practice. Note the eyeshadow as well as the shape of the eye in the top left. Eyelashes on the top one are still way off; I think it's both their size as well as their quantity (way too many individual hairs!).",
],
},
{
title: "Lecturer Confused About Gender",
date: "2026-04-10",
src: "2026/0410.webp",
alt: "A sketch of my lecturer. He has short hair, blue eyes, and is wearing glasses. Next to him is a thinking cloud with gender markers and question marks inside.",
desc: [
"This drawing was inspired by my statistics lecturer saying (in good faith): \"there's male and female, but there's also diverse, which is a lot of genders, well I actually don't know how many\".",
],
},
{
title: "Dyed Hair",
date: "2026-04-17",
src: "2026/0417.webp",
alt: "The head of a character facing away. Their hair is black and red.",
desc: [
"I played around with brushes during my statistics class and made this. There are a few things I like about it. I like that I drew it without outlines and it actually looks decent. I also love the hair colour gradient.",
"It doesn't really make sense for the hair to transition from red at the roots to black and then back to red, but it doesn't matter either. It looks cool.",
],
},
{
date: "2026-04-20",
src: "2026/0420.webp",
alt: "Several pencil sketches including Plankton and Patrick from SpongeBob Squarepants as well as other faces. There's also a close-up of a semi-realistic green eye and a box with a plate and a single sphere on it, with the text 'One Pea 5€'.",
desc: [
"I've not given up on drawing using physical paper and pencils! And I've learned to draw Patrick('s head), for better or for worse.",
"This was drawn during a meeting a student group of mine had at university. I had fun drawing Patrick with different expressions! Plankton ended up not so good.",
"I quite like the more realistic-looking eye, especially the front view. The 'website girl' is me deciding on the head to use for sketches I want to put on this website for some text callouts. Haven't gotten around to drawing these sketches yet, but they'll be cool once I do.",
"The 'One Pea 5€' was me anticipating Tomodachi Life: Living the Dream.",
],
},
{
title: "patrick doing not so good",
date: "2026-04-21",
src: "2026/0421.webp",
alt: "Two drawings of Patrick Star's head. The left is neutral, the right looks exhausted.",
desc: [
"Had some more fun drawing Patrick during my shift. The right one is me at work (I work in IT support).",
],
},
{
date: "2026-04-24",
src: "2026/0424_hand.webp",
alt: "A hand with its fingers spread out. The nails are painted green.",
desc: [
"Another statistics class sketch! I'm bad at hands so I wanted to practice. I was a bit worried doing this, as other people in my class could see what's on my tablet, but I want to get over that feeling of people judging my drawings.",
"I played with shading in this one. It went okay. I didn't really know how to shade. The thumb also ended up too thin.",
],
},
{
title: "Blinn-Phong",
date: "2026-04-24",
src: "2026/0424_ball.webp",
alt: "An orange sphere shaded to look three-dimensional.",
desc: [
"This is the dot from a '!' symbol I wrote in my statistics notes. I decided to try and shade it similarly to how I've seen it in game engines and 3D modelling software -- even with a specular highlight!",
"I like how this turned out! From a distance, it really looks kind of 3D, especially compared to all the other notes on the page.",
],
},
];

View File

@@ -1,13 +1,12 @@
<script lang="ts">
import Content from "$lib/viewport/content.svelte";
import Banner2 from "$lib/banner2.svelte";
import TableOfContents from "$lib/components/table-of-contents.svelte";
import ImageRow from "$lib/media/image-row.svelte";
import ContentSidebar from "$lib/viewport/content-sidebar.svelte";
export let data;
</script>
<svelte:head>
<title>Art Feed | denizk0461</title>
<title>Creative Feed | denizk0461</title>
</svelte:head>
{#snippet pageButtons(currentIndex: number)}
@@ -30,41 +29,35 @@
</div>
{/snippet}
<Content>
<Banner2
title="Creative Feed"
banner="banner.webp"
bannerAlt="Mirror picture of me, pixelated beyond recognition"
subtitle="minor things I have worked on"
/>
<ContentSidebar bannerContent={{
title: "Creative Feed",
banner: "banner.webp",
bannerAlt: "A blue screen with the text 'how do you do art ? 1. face your fears 2. become your heroes'. The 'art' looks to have been edited in. The music artist Porter Robinson is standing in the bottom right corner.",
subtitle: "minor things I have worked on",
}}>
<p>Welcome to my creative feed! It is heavily inspired by <a href="https://deathsurplus.com/">DeathSurplus' art blog</a> definitely go check out his website!</p>
<p>This page is intended to be a contrasting companion to the <a href="/projects"><code>projects</code> page</a>; I'll use this page for smaller things that don't fit the dedicated-page-format.</p>
<!-- <TableOfContents /> -->
{@render pageButtons(data.currentPage)}
{#each data.feedEntries as entry}
<h2>{entry.title}</h2>
<p class="subtitle">{entry.subtitle}</p>
<p class="subtitle">{entry.date}</p>
<svelte:component this={entry.content} />
{#if entry.images && entry.images.length > 0}
<div class="image-gallery">
{#each entry.images as i, index}
<a class="image-gallery-link" href="{entry.path}/{i}.webp">
<img class="image-gallery-img" loading="lazy" src="{entry.path}/{i}.webp" alt={entry.imageAlts[index]}>
</a>
{/each}
</div>
{#each data.feedPosts as post}
<h2>{post.metadata.title}</h2>
<p class="subtitle">{post.metadata.subtitle}</p>
<p class="subtitle">{post.metadata.date}</p>
<svelte:component this={post.content} />
{#if post.metadata.images && post.metadata.images.length > 0}
<ImageRow
images={post.metadata.images}
path={post.metadata.id}
/>
{/if}
{/each}
{@render pageButtons(data.currentPage)}
</Content>
</ContentSidebar>
<style>
.page-button-container {
@@ -115,42 +108,6 @@
background-color: var(--color-background-highlight-alt);
}
.image-gallery {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 4px;
}
.image-gallery-img {
width: 100%;
height: 100%;
display: block;
object-fit: cover;
filter: brightness(60%) saturate(80%);
max-height: 320px;
transition: filter var(--duration-animation) var(--anim-curve),
scale var(--duration-animation) var(--anim-curve);
scale: 1.04;
}
.image-gallery-link {
outline: var(--border-style) var(--border-dash-size) transparent;
border-radius: var(--border-radius);
z-index: 10;
transition: outline-color var(--duration-animation) var(--anim-curve);
overflow: hidden;
margin: 0;
}
.image-gallery-link:hover .image-gallery-img {
filter: brightness(100%) saturate(100%);
scale: 1.0;
}
.image-gallery-link:hover {
outline-color: var(--color-highlight);
}
.subtitle {
font-family: var(--font-mono);
margin: 0;
@@ -168,20 +125,4 @@
.subtitle::after {
content: " -->";
}
@media screen and (max-width: 800px) {
/* .image-gallery-link, .image-gallery-link:hover .image-gallery-img {
border-radius: 0;
} */
.image-gallery-link:hover {
outline-color: transparent;
}
.image-gallery-img {
border-radius: 0;
scale: 1.0;
filter: brightness(100%) saturate(100%);
}
}
</style>

View File

@@ -1,13 +1,9 @@
import { entries } from './feed';
import { entries, type FeedEntry } from './feed';
interface FeedEntry {
interface FeedPost {
path: string;
content: any;
title: string;
subtitle: string;
date: string;
images: string[];
imageAlts: string[];
metadata: FeedEntry;
}
let entriesPerPage = 8;
@@ -21,7 +17,7 @@ export async function load({ params, url }) {
// TODO check if index exceeds maximum permitted and redirect (to max page?)
let feedEntries: FeedEntry[] = [];
let feedPosts: FeedPost[] = [];
let start = (pageIndex - 1) * entriesPerPage;
for (let i = start; i < start + entriesPerPage; i += 1) {
@@ -29,26 +25,23 @@ export async function load({ params, url }) {
if (i >= entries.length) {
break;
}
let pathArray = entries[i].split("/");
let e = entries[i];
let pathArray = e.id.split("/");
let path = `./${pathArray[0]}/${pathArray[1]}`;
// Vite complains if I don't do this even though it's stupid
let page = await import(`./${pathArray[0]}/${pathArray[1]}.md`);
let md = page.metadata;
let date = md.date;
// prevent SvelteKit from being smart and formatting a YYYY-MM-DD string to a Date object or similar
if ((date as string).endsWith("0Z")) {
date = date.split("T")[0];
}
// let date = e.date;
// prevent SvelteKit from being 'smart' and formatting a YYYY-MM-DD string to a Date object or similar
// if ((date as string).endsWith("0Z")) {
// date = date.split("T")[0];
// }
feedEntries.push({
feedPosts.push({
path,
content: page.default,
title: md.title,
subtitle: md.subtitle,
date: date,
images: md.images,
imageAlts: md.imageAlts,
metadata: e,
});
}
@@ -58,6 +51,6 @@ export async function load({ params, url }) {
return {
currentPage,
maxPages,
feedEntries,
feedPosts,
};
}

View File

@@ -1,15 +0,0 @@
---
title: Lecturer Confused About Gender
subtitle: uni doodles
date: 2026-04-10
images:
- drawing
- hinge
imageAlts:
-
-
---
I had my first day of uni in quite a while. For my class in descriptive statistics, I used my drawing tablet to take notes in Krita, which also proved excellent for doodling. This drawing was inspired by my lecturer, in good faith, saying: "there's male and female, but there's also diverse, which is a lot of genders, well I actually don't know how many".
I also noticed that my drawing tablet's hinge broke!! It's very frustrating, though I can fortunately still use it, just without the ability to adjust it (it's just stuck on the max angle).

View File

@@ -0,0 +1,9 @@
I disassembled my Switch because I wanted to check whether the battery was swollen inside. It's had some battery issues (although they seem to have gone away lately). During reassembly, I put too much force on the SD card reader and broke it. Ordered a new part, replaced it, but turns out I broke the board-side connector too. It doesn't grip the ribbon cable like it should. Squishing some cardboard in there to keep the cable connected and level with the board helped. I was worried it would fail, but it's been a couple of days now and it still works flawlessly. Just in time, too; I started playing the new Tomodachi Life and need the storage for all the screenshots and videos I'm capturing.
I also decided to tackle my 2011 3DS again. It broke in 2018: I was playing Super Mario Maker on the couch when it suddenly made a pop sound and turned off. When I disassembled it, I found that not only have I left the 3DS in a dismal state the last time I opened it -- most of the cables weren't even connected, not to mention the missing Y button, the removed battery due to swelling and the broken shoulder buttons --, but the ribbon cable connecting the speakers and top screen backlight had a *tear!* Finally, I knew what the problem may be!
I ordered a replacement cable and tackled the most challenging task imaginable when repairing a 3DS -- threading a cable through the hinge. It's a miracle I didn't destroy another cable in the process. I also replaced the Y button with a 3D printed one I quickly designed in FreeCAD. It works fine enough.
The 3DS works again!! It's been 8 years since it last booted!! I unfortunately don't have its SD card anymore, so the data is gone, but the system works! Truly a miracle.
Only thing left to do now is to fix the [USB-C port on my New 3DS](/projects/electronics/3ds-usb-c/)... it stopped accepting C-to-C connections, although A-to-C still works. Probably just need to resolder or replace the 5.1kΩ resistor.

View File

@@ -1,19 +1,3 @@
---
title: Transparent Phone Back
subtitle: nowhere to hide!
date: 2026-04-11 2026-04-12
images:
- scratching
- halfway
- clear
- final
imageAlts:
- The back of a Samsung Galaxy S20 FE lying face-down on a deskmat. Toothpicks, cotton pads, and a metal spudger are lying nearby.
- A white phone back that's half-scratched off to reveal the clear plastic.
- An entirely clear phone back.
- A Samsung Galaxy S20 FE with a clear back inside a clear phone case. The phone has a sticker in the middle of the back that shows a lantern with leaves around it.
---
Inspired by Nothing phones and cool dbrand skins from way back, I gave my phone a clear back mod! I've grown a bit tired of the white back and wanted to do something cool and unique instead of just buying a new back and sticking that on there. I've been using this phone for 5 years and the glue has been struggling hard to hold onto the back, so I didn't feel bad doing this.
This was arduous and took a lot of time too (5 hours). I used a metal spudger, bamboo toothpicks, and cottons swabs and pads soaked in acetone-free nail polish remover to remove the two paint layers slowly but surely, being careful not to scratch the back too much. It revealed a slightly cloudly but actually decently transparent back that still preserved its slightly pearlescent purple glow!

View File

@@ -1,4 +1,75 @@
export let entries: string[] = [
"electronics/trans-phone",
"drawings/lecturer-gender",
import { type A11yImage } from "$lib/media/a11y-img";
export interface FeedEntry {
id: string;
title: string;
subtitle: string;
date: string;
posted: string;
images: A11yImage[];
}
export let entries: FeedEntry[] = [
{
id: "electronics/nintendo-repairs",
title: "Nintendo Handheld Repairs",
subtitle: "it's been 8 years...",
date: "2026-04-16 2026-04-23",
posted: "2026-04-24",
images: [
{
src: "cardboard.webp",
alt: "A Nintendo Switch with its back removed. The SD card reader connector is obstructed by a pink piece of cardboard.",
},
{
src: "knifetoswitch.webp",
alt: "The SD card reader connector of a Nintendo Switch currently being stabbed with a knife in an attempt at repair.",
},
{
src: "3dsopen.webp",
alt: "A 2011 Nintendo 3DS with its back covers removed on both the top and the bottom half.",
},
{
src: "thread.webp",
alt: "The hinge of a 3DS with some ribbon cables halfway pulled through.",
},
{
src: "threadsuccess.webp",
alt: "The hinge of a 3DS with three ribbon cables successfully pulled through.",
},
{
src: "3dsfunctional.webp",
alt: "A functional 2011 Nintendo 3DS on the home screen, displaying the game 'Zelda: Four Swords Anniversary Edition'",
},
],
},
{
id: "electronics/trans-phone",
title: "Transparent Phone Back",
subtitle: "nowhere to hide!",
date: "2026-04-11 2026-04-12",
posted: "2026-04-12",
images: [
{
src: "scratching.webp",
alt: "The back of a Samsung Galaxy S20 FE lying face-down on a deskmat. Toothpicks, cotton pads, and a metal spudger are lying nearby.",
},
{
src: "halfway.webp",
alt: "A white phone back that's half-scratched off to reveal the clear plastic.",
},
{
src: "clear.webp",
alt: "An entirely clear phone back.",
},
{
src: "final.webp",
alt: "A Samsung Galaxy S20 FE with a clear back inside a clear phone case. The phone has a sticker in the middle of the back that shows a lantern with leaves around it.",
},
],
},
];
export function getLatestPostDate(): string {
return entries[0].posted;
}

View File

@@ -1,8 +1,6 @@
<script lang="ts">
import Banner2 from "$lib/banner2.svelte";
import Content from "$lib/viewport/content.svelte";
import TableOfContents from "$lib/components/table-of-contents.svelte";
import LinkList, { type LinkEntry } from "$lib/lists/link-list.svelte";
import ContentSidebar from "$lib/viewport/content-sidebar.svelte";
let favouriteAlbums: LinkEntry[] = [
{
@@ -108,17 +106,15 @@
</svelte:head>
<Content>
<Banner2
title="About Me & natconf.dev"
banner="/me.webp"
bannerAlt="Mirror picture of me, pixelated beyond recognition"
subtitle="If you'd like to learn more about me and my website"
date="2025-08-10"
dateUpdated="2026-04-03"
pixelated />
<TableOfContents />
<ContentSidebar bannerContent={{
title: "About Me & natconf.dev",
banner: "/me.webp",
bannerAlt: "Mirror picture of me, pixelated beyond recognition",
subtitle: "If you'd like to learn more about me and my website",
date: "2025-08-10",
dateUpdated: "2026-04-03",
pixelated: true,
}}>
<p>Hi there! I'm Deniz (they/them). Welcome to my website!</p>
@@ -199,7 +195,7 @@
<p>When I was recently replaying the original Ratchet & Clank (2002) in German, I noticed again that the dialogue translated into German is often longer than the original English dialogue, which makes characters' voice lines run into one another. This doesn't happen in the English original.</p>
<p>This is most noticeable in the cutscene that plays after you acquire the Hologuise on planet Kalebo III, where <a href="https://youtu.be/XIShUN7AUqg?t=3479">the narrator explains how the Hologuise works</a>. The narrations lags WAY behind the visuals, to the point that there's a brief black screen at the end while the narration is still ongoing, and then it just cuts off the narrator entirely.</p>
</Content>
</ContentSidebar>
<style>
.faq-question {

View File

@@ -19,10 +19,10 @@
<title>Feeds | denizk0461</title>
</svelte:head>
<Content>
<Banner2
title="Feeds"
subtitle="XML feeds" />
<Content bannerContent={{
title: "Feeds",
subtitle: "XML feeds",
}}>
<p>This is a list of RSS feeds I maintain on this website. You can subscribe to them by adding the link of any feed to an RSS reader of your liking.</p>

View File

@@ -8,11 +8,10 @@
<title>Music Rotation | denizk0461</title>
</svelte:head>
<Content>
<Banner2
title="Music Rotation"
dateUpdated="2026-04-14"
/>
<Content bannerContent={{
title: "Music Rotation",
dateUpdated: "2026-04-14",
}}>
<p>content coming soon.</p>

View File

@@ -7,11 +7,10 @@
<title>Now | denizk0461</title>
</svelte:head>
<Content>
<Banner2
title="Now"
dateUpdated="2026-04-14"
/>
<Content bannerContent={{
title: "Now",
dateUpdated: "2026-04-14",
}}>
<p>content coming soon.</p>

View File

@@ -7,11 +7,10 @@
<title>Privacy & Cookies | denizk0461</title>
</svelte:head>
<Content>
<Banner2
title="Information on Privacy & Cookies"
dateUpdated="2025-09-10"
/>
<Content bannerContent={{
title: "Information on Privacy & Cookies",
dateUpdated: "2025-09-10",
}}>
<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>

View File

@@ -31,16 +31,15 @@
<title>Projects | denizk0461</title>
</svelte:head>
<Content>
<Banner2
title="My Disordered Projects"
banner="/projects/banner.webp"
bannerAlt="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."
subtitle="things I have worked on" />
<Content bannerContent={{
title: "My Disordered Projects",
banner: "banner.webp",
bannerAlt: "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.",
subtitle: "things I have worked on",
}}>
<p>Welcome to my 💫new💫 projects page! Here I show off all the things I have done. Projects are ordered reverse-chronologically and have some other neat information displayed. have fun browsing~!</p>
<!-- TODO it would be nice if these were green here to separate them from the red tags on the blog page -->
<div class="tag-filters">
<p class="tag-filter-header"># filter projects by category:</p>
<div class="tag-filter-container">
@@ -123,14 +122,14 @@
width: 100%;
height: 100%;
z-index: -1;
filter: brightness(40%) saturate(40%) blur(1px);
filter: brightness(40%) saturate(40%);
transition: filter var(--duration-animation) var(--anim-curve),
scale var(--duration-animation) var(--anim-curve);
scale: 1.05;
}
.project-wrapper:hover .project-banner {
filter: brightness(60%) saturate(100%) blur(0);
filter: brightness(60%) saturate(100%);
scale: 1.0;
}

View File

@@ -1,7 +1,5 @@
<script>
import Banner2 from "$lib/banner2.svelte";
import Content from "$lib/viewport/content.svelte";
// import TableOfContents from "$lib/components/table-of-contents.svelte";
import ContentSidebar from "$lib/viewport/content-sidebar.svelte";
export let data;
</script>
@@ -12,17 +10,14 @@
<meta name="DCTERMS.created" content="{data.projectDetails.date}T12:00">
</svelte:head>
<Content>
<Banner2
title="{data.projectDetails.title}"
subtitle="{data.projectDetails.subtitle}"
dateIndeterminate="{data.projectDetails.date}"
dateUpdated="{data.projectDetails.dateUpdated}"
banner="{data.projectDetails.banner}"
bannerAlt="{data.projectDetails.bannerAlt}"
/>
<!-- <TableOfContents /> -->
<ContentSidebar bannerContent={{
title: data.projectDetails.title,
subtitle: data.projectDetails.subtitle,
dateIndeterminate: data.projectDetails.date,
dateUpdated: data.projectDetails.dateUpdated,
banner: data.projectDetails.banner,
bannerAlt: data.projectDetails.bannerAlt,
}}>
{#if data.projectDetails.links.length > 0}
<div class="link-button-container">
@@ -34,7 +29,7 @@
<svelte:component this={data.content} />
</Content>
</ContentSidebar>
<style>
.link-button-container {

View File

@@ -1,84 +0,0 @@
<script lang="ts">
import ImageGallery from "$lib/lists/image-gallery.svelte";
</script>
Back in January, I was thinking to myself that I'd really like to learn to draw. It would have some practical benefits like potentially being able to draw concept art for my game, but ultimately I just liked the idea of drawing as a hobby. I doodled a few small things before deciding that I should challenge myself to draw something every single day for an entire month (4 weeks); I documented the entire thing, but spoiler alert: I think I succeeded in learning to draw and I am now able to sketch things I was never able to before!
Here are some of my favourite drawings from around that time, in chronological order:
<ImageGallery
images={[
{
src: "/blog/2026/0129/girl.webp",
alt: "A small drawing of an anime-style girl's head. She has a ponytail and is looking towards the left with a concentrated gaze.",
desc: [
"<b>2026-01-29</b> (before the challenge)",
"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!",
],
},
{
src: "breadgirl.webp",
alt: "An anime-style girl chewing on a piece of bread. She wears a ponytail and a sleeveless top.",
desc: [
"<b>2026-01-30</b> (before the challenge)",
"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.",
],
},
{
src: "/blog/2026/0205/13-2.webp",
alt: "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.",
desc: [
"<b>2026-02-18</b>",
"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!",
],
},
{
src: "/blog/2026/0205/18.webp",
alt: "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.",
desc: [
"<b>2026-02-23</b>",
"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.",
],
},
{
src: "/blog/2026/0205/21.webp",
alt: "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.",
desc: [
"<b>2026-02-26</b>",
"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.",
],
},
{
src: "/blog/2026/0205/22.webp",
alt: "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.",
desc: [
"<b>2026-02-27</b>",
"Possibly my favourite sketch from the drawing challenge, because she looks cool, but also because her design deviates from the other characters a bit.",
],
},
{
src: "/blog/2026/0205/27-1.webp",
alt: "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.",
desc: [
"<b>2026-03-04</b>",
"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!",
],
},
{
src: "0310.webp",
alt: "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.",
desc: [
"<b>2026-03-10</b> (after the challenge)",
"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.",
],
},
]}
/>

View File

@@ -1,5 +1,6 @@
<script lang="ts">
import TableOfContents from "$lib/components/table-of-contents.svelte";
import ImageRow from "$lib/media/image-row.svelte";
</script>
![Top view of the Daisy FM synth](fullview.webp)
@@ -35,24 +36,39 @@ I designed the PCB in [KiCad](https://www.kicad.org/). I had no prior experience
I split the PCB into four layers, separating power, ground, digital signals (switches and toggle), and analogue signals (potentiometers, audio out). I made some exceptions, such as for the waveform buttons, since they cross the playing key traces.
<div class="horizontally-centre-aligned">
<img src="pcb-sketch.webp" alt="Screenshot of KiCad schematic">
<img src="pcb-empty.webp" alt="The finished PCB produced from the KiCad schematic">
</div>
<ImageRow
images={[
{
src: "pcb-sketch.webp",
alt: "Screenshot of KiCad schematic",
},
{
src: "pcb-empty.webp",
alt: "The finished PCB produced from the KiCad schematic",
},
]}
/>
The PCB was manufactured by [JLCPCB](https://jlcpcb.com/).
#### USB-C
If using a USB-C breakout board, wire in two 5.1 kΩ pulldown resistors by soldering a resistor between the pin CC1 and ground. Do the same for pin CC2 with a second resistor.
Alternatively, if your USB-C breakout board does not have CC pins exposed, there's a chance you could still connect pulldown resistors to make it compatible. On [this breakout board](https://www.amazon.de/dp/B09FPZDDD9) I purchased on Amazon, I discovered that the third pin from the right exposes a CC connection. Wiring a resistor between this pin and ground enabled C-to-C functionality. If you want to test the functionality before soldering, you can hold a resistor between this pin and either the ground connector on the board or the USB port's outer shell, since that one's grounded as well.
<div class="horizontally-centre-aligned">
<img src="usbc-breakout-small.webp" alt="Close-up of the USB-C breakout board with the CC pin marked">
<img src="hand.webp" alt="The tiny USB-C breakout board compared to my hand">
</div>
<ImageRow
images={[
{
src: "usbc-breakout-small.webp",
alt: "Close-up of the USB-C breakout board with the CC pin marked",
},
{
src: "hand.webp",
alt: "The tiny USB-C breakout board compared to my hand",
},
]}
/>
Do keep in mind that this type of connector does not have mounting holes and requires either a more sophisticated mounting mechanism in the chassis, hot glue, or both. Also, soldering to this pin is insanely finicky, since it is VERY small, so I strongly recommend a breakout board such as [this one by Soldered](https://www.reichelt.de/entwicklerboards-usb-typ-c-adapterboard-buchse-debo-usb-c-f-p376522.html) that exposes the CC pins.

View File

@@ -24,22 +24,32 @@ Software-wise, I set this up with the original deej software to control main vol
{
src: "prototype.webp",
alt: "An Arduino Pro Micro with a white LED attached to it. The LED is glowing.",
desc: ["initial prototype to test LED functionality"],
desc: [
"I initially tested whether I could get the Arduino to light up an LED. I wanted to include LEDs because I thought I could later integrate them in a cool way, but the device ended up being so simple, there wasn't really a need for much visual feedback.",
],
},
{
src: "soldering.webp",
alt: "An Arduino with keyboard switches, LEDs, and a slider wired into it. The components are connected with relatively long cables.",
desc: ["it may look like a prototype, but these are the production-ready innards of the machine!"],
desc: [
"It may look like a prototype, but these are the production-ready innards of the machine! Trying to put this into the 3D printed shell without breaking any of the frail soldering joints was a pain, especially as the key switches really weren't meant for soldering and thus barely hold on to the wires.",
],
},
{
src: "printing.webp",
alt: "A Bambu Lab A1 mini 3D printer in the middle of printing casing parts using a golden filament. The printer head has two googly eyes attached.",
desc: ["googly-eyed printer hard at work"],
desc: [
"googly-eyed printer hard at work.",
"I'm using Bambu Lab PLA Metal 'Iridium Gold Metallic (13400)' for the body here.",
],
},
{
src: "assembly.webp",
alt: "An Arduino set into a 3D printed case with a slider, two LEDs, and four key switches soldered to it using wires. The components are spread out and hanging out the top of the case.",
desc: ["no PCB? no problem"],
desc: [
"A visual guide to the (mental) pain I had to endure assembling this thing. Creating a simple PCB would have been MUCH better, but it would have meant designing one in KiCad, paying for at least 5 of them at a supplier, waiting for them to ship, etc. ...",
"Safe to say, I wasn't willing to do any of that. Quick and cheap was my preferred method here.",
],
},
]}
/>

View File

@@ -32,11 +32,11 @@
<title>Homesick | denizk0461</title>
</svelte:head>
<Content>
<Banner2
title="Homesick"
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." />
<Content bannerContent={{
title: "Homesick",
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>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>

View File

@@ -1,5 +1,6 @@
<script lang="ts">
import Video from "$lib/video.svelte";
import ImageRow from "$lib/media/image-row.svelte";
</script>
My progress in October 2023. Updates are shown in chronological order.
@@ -40,10 +41,18 @@ We now have health points! As it is currently set up, the health bar supports up
The health bar even aligns properly for 5 and 6 max health points, reducing the max points displayed per line from 4 to 3!
<div class="horizontally-centre-aligned">
<img src="2023-10-07_01.webp" alt="Five health points in the shape of plusses">
<img src="2023-10-07_02.webp" alt="Six health points in the shape of plusses">
</div>
<ImageRow
images={[
{
src: "2023-10-07_01.webp",
alt: "Five health points in the shape of plusses",
},
{
src: "2023-10-07_02.webp",
alt: "Six health points in the shape of plusses",
},
]}
/>
## What Are the Controls??
@@ -71,11 +80,22 @@ The Quick Select menu opens when the E key (keyboard) or the North face button (
In order to get started with the 3D models I'll need to create for the game, I attempted to begin the process of modelling the weapon of the protagonist! It's supposed to become a battle axe, though I have not yet finalised whether I'll keep with the idea.
<div class="horizontally-centre-aligned width-restricted">
<img src="2023-10-22_02.webp" alt="Top view of the battle axe with a flat blade">
<img src="2023-10-22_04.webp" alt="Top view of the battle axe with a thickened blade">
<img src="2023-10-22_05.webp" alt="Side view of the battle axe">
</div>
<ImageRow
images={[
{
src: "2023-10-22_02.webp",
alt: "Top view of the battle axe with a flat blade",
},
{
src: "2023-10-22_04.webp",
alt: "Top view of the battle axe with a thickened blade",
},
{
src: "2023-10-22_05.webp",
alt: "Side view of the battle axe",
},
]}
/>
## Hot, Fresh Quality

View File

@@ -1,5 +1,6 @@
<script lang="ts">
import Video from "$lib/video.svelte";
import ImageRow from "$lib/media/image-row.svelte";
</script>
My progress in November 2023. Updates are shown in chronological order.
@@ -10,11 +11,22 @@ Lots of progress on the 3D models! I modelled the first weapon for the game, the
The earliest version was based on an 8-sided cylinder. After some feedback from friends, I remade the weapon, using a 16-sided cylinder, and also adding more details to the weapon overall. More attention went into the grip, which now resembled a weapon grip more so than a stick.
<div class="horizontally-centre-aligned">
<img src="2023-11-05_00.webp" alt="N5 Blaster v1 made from an 8-sided cylinder">
<img src="2023-11-03_01.webp" alt="N5 Blaster v2 made from a 16-sided blaster and with additional lights on the grip">
<img src="2023-11-05_03.webp" alt="N5 Blaster v3 with additional lights on the body">
</div>
<ImageRow
images={[
{
src: "2023-11-05_00.webp",
alt: "N5 Blaster v1 made from an 8-sided cylinder",
},
{
src: "2023-11-03_01.webp",
alt: "N5 Blaster v2 made from a 16-sided blaster and with additional lights on the grip",
},
{
src: "2023-11-05_03.webp",
alt: "N5 Blaster v3 with additional lights on the body",
},
]}
/>
Here's an overview of the first model.
@@ -32,11 +44,22 @@ In my first attempt at creating this icon, I took a picture of the wireframed ic
I also continued work on the battle axe, giving it more character. It's still not close to being finished, but it's now a bit less of a rough draft.
<div class="horizontally-centre-aligned">
<img src="2023-11-01_01.webp" alt="Battle axe with a hollowed-out blade">
<img src="2023-11-01_06.webp" alt="Battle axe with a stabilising pattern in the blade">
<img src="2023-11-01_09.webp" alt="Battle axe with two blades">
</div>
<ImageRow
images={[
{
src: "2023-11-01_01.webp",
alt: "Battle axe with a hollowed-out blade",
},
{
src: "2023-11-01_06.webp",
alt: "Battle axe with a stabilising pattern in the blade",
},
{
src: "2023-11-01_09.webp",
alt: "Battle axe with two blades",
},
]}
/>
I will admit though that I'm unsure whether I'll actually stick with the battle axe as the protagonist's main melee weapon.
@@ -46,10 +69,18 @@ Another idea, though more as an unlockable extra, is Derek the crowbar.
I also worked on the upgrade for the N5 Blaster, the N5 Cannon. Progress on that one has been a bit slow, since I have yet to figure out what kind of weapon I want the upgraded version to be.
<div class="horizontally-centre-aligned">
<img src="2023-11-12_04.webp" alt="N5 Cannon body">
<img src="2023-11-12_02.webp" alt="N5 Cannon Body next to the N5 Blaster">
</div>
<ImageRow
images={[
{
src: "2023-11-12_04.webp",
alt: "N5 Cannon body",
},
{
src: "2023-11-12_02.webp",
alt: "N5 Cannon Body next to the N5 Blaster",
},
]}
/>
And, as a bonus, here's the discarded, very-early-WIP draft I created for a rifle-type weapon. I don't think this type of weapon fits the type of game I'm making.
@@ -63,10 +94,18 @@ And a draft of a rocket launcher with 9 barrels! This is heavily inspired by the
I begun modelling my protagonist! I didn't progress far, as I currently lack a vision for where I really want my character to go in detail. I have slight ideas inspirations are, for example, [Merc & Green from Ratchet: Gladiator](https://hero.fandom.com/wiki/Merc_and_Green), and [Denholm Reynholm](denholm.webp). I quite liked the idea of having glowing tubes on the character's back; I got the inspiration from a Blender tutorial that was randomly recommended to me one morning.
<div class="horizontally-centre-aligned">
<img src="2023-11-11_05.webp" alt="Tubes for the back of the protagonist">
<img src="2023-11-12_01.webp" alt="Glowing tubes attached to a torso block">
</div>
<ImageRow
images={[
{
src: "2023-11-11_05.webp",
alt: "Tubes for the back of the protagonist",
},
{
src: "2023-11-12_01.webp",
alt: "Glowing tubes attached to a torso block",
},
]}
/>
i love

View File

@@ -1,5 +1,6 @@
<script lang="ts">
import Video from "$lib/video.svelte";
import ImageRow from "$lib/media/image-row.svelte";
</script>
This is my final update before the next semester begins! I'm actually quite sad about this. For the past few days, I've been really motivated to work on my game. The free time that I had after I finished my last few submissions for university gave me enough room to spend significant time developing. But now that the next semester is about to begin, I fear that university will rob me of a significant amount of my time, and that I will not be able to spend the rest of my time productively.
@@ -65,10 +66,18 @@ Something visual to intersperse the dry code explanations:
This is an image for a weapon I've come up recently! It's supposed to be a slow-firing but strong blaster, contrasting the rapid-firing but weaker N5 Blaster (which recently changed to an automatic firing mode). The Venom originated from sketches like this, by the way:
<div class="horizontally-centre-aligned">
<img alt="A REALLY crude sketch of a blaster." src="dual_venom_sketch.webp">
<img alt="A crude sketch of a blaster resembling the 3D model shown above." src="venom_sketch.webp">
</div>
<ImageRow
images={[
{
src: "dual_venom_sketch.webp",
alt: "A REALLY crude sketch of a blaster.",
},
{
src: "venom_sketch.webp",
alt: "A crude sketch of a blaster resembling the 3D model shown above.",
},
]}
/>
The bolt visible in the first sketch actually makes me consider using [the model I showed off recently](/projects/projectn5/devlog/2024/0323/#a-new-weapon-is-in-the-works-) as the v2 for the Venom (which I would call Antidote by the way, in reference to [this song](https://youtu.be/fbafd6UV3w4)).

View File

@@ -1,3 +1,7 @@
<script lang="ts">
import ImageRow from "$lib/media/image-row.svelte";
</script>
I'M BACK!!!!
For real this time!! I've been busy with university, then I started working on other projects including 3D prints, electronics, a different game, then got busy with university again, and now I FINALLY started work on Project N5 again. And, as I promised in [my last update](/projects/projectn5/devlog/2024/0713), it would not take me another 3 months to get back to development in fact, I undercut that deadline by a whole 24 hours!
@@ -26,10 +30,18 @@ There's also a blue line shooting out of the gun in the picture above. That's a
The N5 Blaster received an overhauled icon. The soon-to-be-implemented N5 Bomb Launcher also received its own icon!
<div class="horizontally-centre-aligned">
<img src="n5-blaster-icon.webp" alt="N5 Blaster logo made from an icosphere and two handles">
<img src="n5-bomb-launcher-icon.webp" alt="N5 Bomb Launcher logo made from an icosphere and waves">
</div>
<ImageRow
images={[
{
src: "n5-blaster-icon.webp",
alt: "N5 Blaster logo made from an icosphere and two handles",
},
{
src: "n5-bomb-launcher-icon.webp",
alt: "N5 Bomb Launcher logo made from an icosphere and waves",
},
]}
/>
I changed the icosphere by tracing its 3D counterpart (the ball inside the N5 Blaster's glass tube) from a different point of view. It wasn't symmetrical before it is now.

View File

@@ -1,3 +1,7 @@
<script lang="ts">
import ImageRow from "$lib/media/image-row.svelte";
</script>
I have lots progress to share!!
First things first: Laura is, unlike I promised before, not yet finished. However, I have made *so much* progress in the past few weeks that I just wanted to get out already. Here's the current iteration of Laura:
@@ -36,17 +40,34 @@ Modelling hair has proven... challenging. What style to go with? What modelling
The first try (seen above) was using the technique from the SELS video selecting faces from the character's head, duplicating them, separating them into their own mesh, changing the scale, adding a solidify modifier, and then adding faces. This... worked, but I didn't like the results. And I tried quite a few styles.
<div class="horizontally-centre-aligned">
<img src="laura-hair-flat-1.webp" alt="White hair with middle split">
<img src="laura-hair-flat-2.webp" alt="White hair with spiky bangs">
<img src="laura-hair-flat-3.webp" alt="White hair with spiky ends">
</div>
<div class="horizontally-centre-aligned">
<img src="laura-hair-flat-4.webp" alt="Brown hair with middle split, flowing behind the character's ears">
<img src="laura-hair-flat-5.webp" alt="Brown hair with middle split and divided at the ears">
<img src="laura-hair-flat-6.webp" alt="Brown hair with big bangs">
</div>
<ImageRow
images={[
{
src: "laura-hair-flat-1.webp",
alt: "White hair with middle split",
},
{
src: "laura-hair-flat-2.webp",
alt: "White hair with spiky bangs",
},
{
src: "laura-hair-flat-3.webp",
alt: "White hair with spiky ends",
},
{
src: "laura-hair-flat-4.webp",
alt: "Brown hair with middle split, flowing behind the character's ears",
},
{
src: "laura-hair-flat-5.webp",
alt: "Brown hair with middle split and divided at the ears",
},
{
src: "laura-hair-flat-6.webp",
alt: "Brown hair with big bangs",
},
]}
/>
It always looked too flat, too shapeless, too boring, wrong cuts. It just didn't work.
@@ -54,19 +75,35 @@ Next up: a technique shown in these two videos: [Blender: How to Make HAIR, Full
Essentially, you create a curve and a circle, use the circle's shape as a profile for the curve, then change the circle's shape as well as the position and scale of the curve's vertices to create individual hair strands. Shown well in the two videos linked above, this can look pretty amazing! Only one issue: I'm creating a **game** character, and this technique is quite expensive, as it creates a lot of polygons for all the individual hair strands and the detail that goes into them. To mitigate this, I lowered the resolution of the profiling and used only a few curves to create an entire head's worth of hair. This looked a little like this:
<div class="horizontally-centre-aligned">
<img src="laura-hair-curves.webp" alt="Toon shaded view at the character with brown hair">
<img src="laura-hair-curves-2.webp" alt="Side view at the character without special shading">
</div>
<ImageRow
images={[
{
src: "laura-hair-curves.webp",
alt: "Toon shaded view at the character with brown hair",
},
{
src: "laura-hair-curves-2.webp",
alt: "Side view at the character without special shading",
},
]}
/>
This hair mesh originally (left picture) consisted of three parts: two curves at the front (left/right) and one in the back. This... was okay, but scaling the curves made the hair look weird. Thinner strands, especially when there's only a few of them, made them look more like dreads, and scaling up the vertices to large scales, as seen in the front near the top of the head, makes the hair look as if it's ballooning. Getting the shape right was a mess too: using only a single curve in the back meant that I had exactly one curve to cover quite literally half the head, and making sure that this singular strand of hair covered the head stretching from one ear to another was a pain. I tried using five curves (right picture), so that I have three in the back, but it didn't improve anything.
I then went *back* to the first method of scaling up faces from the head, with more knowledge and several tries behind me, and you know what? It actually kind of worked out.
<div class="horizontally-centre-aligned">
<img src="laura-hair-flat-new-2.webp" alt="Front view at the character with a green headband and bangs">
<img src="laura-hair-flat-new-3.webp" alt="Front view at the character with a green headband and middle-split hair">
</div>
<ImageRow
images={[
{
src: "laura-hair-flat-new-2.webp",
alt: "Front view at the character with a green headband and bangs",
},
{
src: "laura-hair-flat-new-3.webp",
alt: "Front view at the character with a green headband and middle-split hair",
},
]}
/>
The right picture is the current iteration of Laura's hair. I added a head band because I thought it looked nice, though that detail is not final.
@@ -98,10 +135,18 @@ Influenced while I was researching a tangential topic on a Blender forum, I read
To illustrate my point (get it?), here are some pictures. Left is in T-pose, right is in A-pose:
<div class="horizontally-centre-aligned">
<img src="deform-1.webp" alt="Diagram showing a t-posing character, with the text 'large angle, thus more deformation'">
<img src="deform-2.webp" alt="Diagram showing an a-posing character, with the text 'smaller angle, thus less deformation'">
</div>
<ImageRow
images={[
{
src: "deform-1.webp",
alt: "Diagram showing a t-posing character, with the text 'large angle, thus more deformation'",
},
{
src: "deform-2.webp",
alt: "Diagram showing an a-posing character, with the text 'smaller angle, thus less deformation'",
},
]}
/>
However, after watching [this video on bind poses](https://youtu.be/FXfc4Gyw6I0) by Doodley, it seems that... it doesn't really matter. Whether you use the T-pose, the A-pose, the lovingly-called hug-pose, or anything else really depends on what you plan to do with your character. Since Laura will mostly wield guns and keep her arms fairly low for most of the game, I decided to change Laura's modelling pose to an A-pose, with her arms pointed 30 degrees downward.

View File

@@ -1,13 +1,26 @@
<script lang="ts">
import ImageRow from "$lib/media/image-row.svelte";
</script>
While I've been busy working on another game with friends lately, I've managed to almost completely finish Laura. Here's what I've achieved!
## Visual Personality Adjustment
As promised before, I've worked on Laura's head a bit more. Her full face shield has been replaced with a face mask / respirator covering only the bottom half of her face. Also, I finally got the hair into a state I'm actually happy with. Here's a comparison:
<div class="horizontally-centre-aligned">
<img src="../../2024/1222/laura-hair-flat-new-3.webp" alt="The old protagonist's head with green clothing and full-face mask">
<img src="laura-head-new.webp" alt="The new protagonist Laura's head with red clothing, a half-face mask. The character now has a brown left eye, a mechanical right eye, and eyebrows, as well as bangs">
</div>
<ImageRow
images={[
{
src: "../../2024/1222/laura-hair-flat-new-3.webp",
alt: "The old protagonist's head with green clothing and full-face mask",
},
{
src: "laura-head-new.webp",
alt: "The new protagonist Laura's head with red clothing, a half-face mask. The character now has a brown left eye, a mechanical right eye, and eyebrows, as well as bangs",
},
]}
path="."
/>
The eyes took some work to get right, but I'm pretty happy with the current result. They're not proper eyeballs, but instead they're embedded into the head, which visually isn't significant because the flat shading would hide these details anyway. She has a brown left eye with a small sparkle, as well as a right eye replacement. This implies that Laura sustained further damage to the right side of her body, which necessitated replacement of her eye in addition to her right arm.
@@ -56,17 +69,34 @@ I've been thinking of adding an option (perhaps as a cheat code) to change her o
## Some Funny Pictures
<div class="horizontally-centre-aligned">
<img src="ok.webp" alt="An OK hand">
<img src="dance.webp" alt="Laura flailing her arms">
<img src="naruto.webp" alt="Laura naturo-running">
</div>
<div class="horizontally-centre-aligned">
<img src="shock.webp" alt="Laura's face looking shocked">
<img src="reprehension.webp" alt="Laura's face looking astonished">
<img src="disgust.webp" alt="Laura's face looking disgusted">
</div>
<ImageRow
images={[
{
src: "ok.webp",
alt: "An OK hand",
},
{
src: "dance.webp",
alt: "Laura flailing her arms",
},
{
src: "naruto.webp",
alt: "Laura naturo-running",
},
{
src: "shock.webp",
alt: "Laura's face looking shocked",
},
{
src: "reprehension.webp",
alt: "Laura's face looking astonished",
},
{
src: "disgust.webp",
alt: "Laura's face looking disgusted",
},
]}
/>
## The Future of this Devlog

View File

@@ -1,5 +1,6 @@
<script lang="ts">
import Video from "$lib/video.svelte";
import ImageRow from "$lib/media/image-row.svelte";
</script>
I've been making a lot of progress in a lot of different areas, so I won't be able to elaborate on every little detail, but I'll focus on more major things. Excited to share what I've been working on!
@@ -84,12 +85,26 @@ I also fixed a long-running bug where the `DirectionalLight3D` of this preview w
### Please Appreciate These Wonderful Temporary Weapon Icons
<div class="horizontally-centre-aligned">
<img src="104-icon.webp" alt="An icon for a rocket launcher that looks like a sperm">
<img src="106-icon.webp" alt="A primitive flame as an icon for a flame thrower">
<img src="107-icon.webp" alt="A hand-drawn crosshair serving as an icon for a rifle">
<img src="108-icon.webp" alt="The words 'VENOM' as an icon for the weapon of that name">
</div>
<ImageRow
images={[
{
src: "104-icon.webp",
alt: "An icon for a rocket launcher that looks like a sperm",
},
{
src: "106-icon.webp",
alt: "A primitive flame as an icon for a flame thrower",
},
{
src: "107-icon.webp",
alt: "A hand-drawn crosshair serving as an icon for a rifle",
},
{
src: "108-icon.webp",
alt: "The words 'VENOM' as an icon for the weapon of that name",
},
]}
/>
## Grand Code Overhaul

View File

@@ -1,7 +1,5 @@
<script>
import Banner2 from "$lib/banner2.svelte";
import Content from "$lib/viewport/content.svelte";
import TableOfContents from "$lib/components/table-of-contents.svelte";
import ContentSidebar from "$lib/viewport/content-sidebar.svelte";
export let data;
</script>
@@ -12,18 +10,14 @@
<meta name="DCTERMS.created" content="{data.date}">
</svelte:head>
<Content>
<Banner2
title="{data.title}"
subtitle="Homesick Devlog"
date="{data.date}"
banner="preview.webp"
bannerAlt="{data.bannerAlt}"
/>
<TableOfContents />
<ContentSidebar bannerContent={{
title: data.title,
subtitle: "Homesick Devlog",
date: data.date,
banner: "preview.webp",
bannerAlt: data.bannerAlt,
}}>
<svelte:component this={data.content} />
</Content>
</ContentSidebar>

View File

@@ -22,7 +22,6 @@ export interface Link {
export enum ProjectCategory {
NULL = "all",
DRAWINGS = "drawings",
GAMES = "games",
ELECTRONICS = "electronics",
MUSIC = "music",
@@ -107,24 +106,6 @@ export const projects: Project[] = [
}
],
},
{
category: ProjectCategory.DRAWINGS,
id: "firstmonth",
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.",
title: "My First Month Drawing",
subtitle: "self-imposed drawing challenge",
description: "",
isOngoing: false,
date: "February March 2026",
status: ProjectStatus.FINISHED,
links: [
{
text: "accompanying blog post",
link: "/blog/2026/0205",
}
],
},
// 2025
{

View File

Before

Width:  |  Height:  |  Size: 932 KiB

After

Width:  |  Height:  |  Size: 932 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

View File

Before

Width:  |  Height:  |  Size: 131 KiB

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 KiB