Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion content/about/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Hi, I'm Yifei (逸飞)!

This is my personal website where I maintain a very cool blog and share various photos I've taken on my camera (FujiFilm X100) and on my smartphone (Samsung Galaxy S9).

This website doesn't use any JavaScript or cookies, just some plain HTML and CSS built with Hugo, hosted on GitHub Pages. The website theme was designed and coded by myself ! :D !
This website doesn't use any cookies, and for the most part doesn't use any JavaScript. Pages that do contain scripting will indicate so at the top of the page. This website is built with Hugo, and is hosted on GitHub Pages. The website theme was designed and coded by myself ! :D !

## life

Expand Down
4 changes: 3 additions & 1 deletion content/gallery/hypnosis.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ location: "University of Waterloo, SCH to Arts Tunnel"
imageUrl: "https://live.staticflickr.com/65535/53713021703_83ddf8ec50_o.jpg"
width: 2870
height: 4310
---
---

One time during university, my friend and I 1v1'ed each other here in a fierce battle of the [penis game](https://www.urbandictionary.com/define.php?term=the+penis+game).
2 changes: 1 addition & 1 deletion content/gallery/solar-eclipse.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ width: 4310
height: 2870
---

First in my life experiencing a total solar eclipse, and holy shit that was magical. Unfortunately I didn't have a zoom lens so this photo hopefully captures more accurately what the real experience is like seeing the sun through eclipse glasses.
First time in my life experiencing a total solar eclipse, and holy shit was that magical. Unfortunately I didn't have a zoom lens so this photo hopefully captures more accurately what the real experience is like seeing the sun through eclipse glasses.

A few weeks before the eclipse, I suggested to one of my good friends that we organize a small trip to see the total solar eclipse. That small group ended up becoming 10 or so people, and we struggled for a while coming to a decision on where to go. Initially we were thinking of going to Niagara Falls. It was right under the path of totality, and there were a lot of fun things to do in the area while we waited. Buuuuut eventually we all came to realize that the other six million people living in the GTA were all going to go there as well, so we figured we'd go somewhere else with hopefully fewer people. Spoiler alert: Niagara Falls ended up being completely clouded out for the entire eclipse ! :D !

Expand Down
46 changes: 46 additions & 0 deletions content/posts/hashing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
title: "SHA-256"
date: 2025-03-05
draft: false
scripts: ["hash.js"]
stylesheets: ["hash"]
---

*The data visualizations in this post may be more legible on a light colour-scheme.*

I had this sudden idea to visualize each and every single bit of a SHA-256 hash in a heatmap-style visual, not really sure where the inspiration came from.

We generally know that SHA-256 is pretty secure (for now), but aside from just trusting that it works, I've never really stopped to understand nor proven to myself that it truly is secure.

Now I'm not really a mathematician, but I _do_ have some rapidly decaying leftover memories from [ECE 307 - Probability Theory and Statistics 2](https://ucalendar.uwaterloo.ca/2324/COURSE/course-ECE.html#ECE307), so let's run some un-scientific tests to see if we can find any predictable patterns in how SHA-256 hashes are computed.

One of the craziest things to me is that this algorithm is supposed to be able to generate two virtually indistinguishable hashes from two nearly-identical strings. You'd think that the difference between `0x61` ('a') and `0x63` ('c') is pretty minute right? Bit-wise, they differ by 1 bit-flip in the 2nd least significant bit.

```
'a' -> 0x61 -> 01100001
'c' -> 0x63 -> 01100011
```

Below, I've displayed two heatmaps for the SHA-256 hashes of both 'a' and 'c'. Each column of the heatmap corresponds to one byte of the 32 byte long hash, and each cell corresponds to one of the eight bits in that byte. The left-most column displays the most significant byte, and the top of each column represents the most significant bit.

{{< hash id="hash-a" caption="SHA-256 hash of UTF-8 string 'a'" >}}
<br>
{{< hash id="hash-c" caption="SHA-256 hash of UTF-8 string 'c'" >}}

It's pretty hard to compare these two visualizations at a glance, so let's overlay them on top of each other.

{{< hash id="hash-a-c" caption="SHA-256 hashes of UTF-8 string 'a' & 'c' overlaid on top of each other" >}}

I don't know about you, but I don't really see any obvious patterns going on here. The

# Playground

Before we begin, here's a little interactive sandbox to compute the SHA-256 hash of any text.

{{< hash id="table" >}}
<table id="hash" class="hash-grid">
<tr><td><div></div></td><td><div></div></td><td><div></div></td><td><div></div></td><td><div></div></td><td><div></div></td><td><div></div></td><td><div></div></td><td><div></div></td><td><div></div></td><td><div></div></td><td><div></div></td><td><div></div></td><td><div></div></td><td><div></div></td><td><div></div></td><td><div></div></td><td><div></div></td><td><div></div></td><td><div></div></td><td><div></div></td><td><div></div></td><td><div></div></td><td><div></div></td><td><div></div></td><td><div></div></td><td><div></div></td><td><div></div></td><td><div></div></td><td><div></div></td><td><div></div></td><td><div></div></td></tr>
</table>
<input type="text" oninput="update(this.value);" id="input" placeholder="type here" autocomplete="off">

Now that that's out of the way, let's begin with test #1.
20 changes: 20 additions & 0 deletions content/posts/sdvx-controller.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
title: "Sound Voltex Controller: Embedded Systems Project (Ongoing)"
date: 2024-07-05
draft: true
---

## Intro

I've decided to start making my own personal [Sound Voltex](https://remywiki.com/What_is_SOUND_VOLTEX) rhythm game controller. It'll be a project that involves electronics, maybe some PCB design, embedded programming, and 3D design.

## Why

While I was co-oping in Tokyo, I found myself visiting rhythm game arcades a lot. A few games that I really stuck with were *Chunithm*, *Dance Dance Revolution*, and *Sound Voltex*.

## Parts

I haven't yet picked out specific parts. I'll need:

- 2 rotary encoders (100-200 PPR minimum)
- 7 buttons (probably mechanical key switches)
7 changes: 6 additions & 1 deletion hugo.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,9 @@ menus:
weight: 20
- name: github
url: "https://github.com/yfxu"
weight: 30
weight: 30
markup:
goldmark:
renderer:
unsafe: true
enableGitInfo: true
118 changes: 118 additions & 0 deletions themes/ennui/assets/js/hash.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
const textEncoder = new TextEncoder();
const table = document.getElementById("table");
const rows = table.getElementsByTagName("tr");
const hashRow = document.getElementById("hash").children[0].children[0].children;

async function update(text) {
const hash = await crypto.subtle
.digest("SHA-256", textEncoder.encode(text))
.then((hashBuffer) => {
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray;
});
var i = 0;
hash.forEach((byte) => {
for (var j = 0; j < 8; j++) {
var bitwiseAnd = byte & Math.pow(2, j);
if (bitwiseAnd > 0) {
rows[7 - j].children[i].children[0].className = "set";
} else {
rows[7 - j].children[i].children[0].className = "";
}
}
i++;
});

for (var i = 0; i < 32; i++) {
var byteString = hash[i].toString(16).padStart(2, "0");
hashRow[i].children[0].innerHTML = byteString;
}
}

async function draw(id, texts) {
const staticTable = document.getElementById(id);
const staticRows = staticTable.getElementsByTagName("tr");

const hashes = texts.map(async (text) => {
return await crypto.subtle
.digest("SHA-256", textEncoder.encode(text))
.then((hashBuffer) => {
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray;
})
});

var opacityValues = [
[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
],
[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
],
[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
],
[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
],
[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
],
[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
],
[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
],
[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
],
];

Promise.all(hashes).then((values) => {
values.forEach((hash) => {
var i = 0;
hash.forEach((byte) => {
for (var j = 0; j < 8; j++) {
var bitwiseAnd = byte & Math.pow(2, j);
if (bitwiseAnd > 0) {
staticRows[7 - j].children[i].children[0].className = "set";
opacityValues[7 - j][i] += 1 / texts.length;
}
}
i++;
});
});

for (var i = 0; i < 8; i++) {
for (var j = 0; j < 32; j++) {
staticRows[i].children[j].children[0].style.opacity =
opacityValues[i][j];
}
}
});
}

document.addEventListener(
"DOMContentLoaded",
function () {
update("");
draw("hash-a", ["a"]);
draw("hash-c", ["c"]);
draw("hash-a-c", ["a", "c"]);
},
false
);

window.textEncoder = textEncoder;
window.rows = rows;
window.update = update;
window.hashRow = hashRow;
14 changes: 9 additions & 5 deletions themes/ennui/assets/scss/_variables.scss
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
:root {
--color-background: #ffffff;
--color-background: #f0f2f8;
--color-background-secondary: #e9e9e9;
--color-background-tertiary: #ffffff;
--color-text-primary: #3b393d;
--color-text-secondary: #a6a9ad;
--color-accent: #45b694;
--color-text-secondary: #929497;
--color-accent: #3d37ec;
--max-width: 600px;
--mobile-width: 450px;
}

@media (prefers-color-scheme: dark) {
:root {
--color-background: #2B2A33;
--color-background: #2b2a33;
--color-background-secondary: #323238;
--color-background-tertiary: #26242b;
--color-text-primary: #ede3dd;
--color-text-secondary: #747a7a;
--color-accent: #03CEA4;
--color-accent: #5a5ae7;
}
}
58 changes: 58 additions & 0 deletions themes/ennui/assets/scss/hash.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
table.hash-grid {
width: calc(100% + 4px);
border-collapse: separate;
border-spacing: 2px;
margin: 0 -2px;

td {
padding: 0;
background-color: var(--color-background-secondary);

div {
font-family: monospace;
font-size: 0.625rem;
text-align: center;
aspect-ratio: 1;
padding: 0;
margin: 0;

&.set {
background-color: var(--color-accent);
}
}
}

caption {
caption-side: bottom;
font-size: 0.75rem;
font-style: italic;
}
}


table#hash td {
line-height: 1rem;
height: auto;
background-color: var(--color-background-tertiary);
}

div.centre {
display: flex;
justify-content: center;
}

input#input {
width: 100%;
height: 1.5rem;
text-align: center;
outline: none;
border: none;
margin: auto;
background-color: var(--color-background-secondary);
color: var(--color-text-primary);
padding: 0;
}

input#input:focus {
outline: none;
}
31 changes: 26 additions & 5 deletions themes/ennui/assets/scss/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,18 @@ body {
min-height: 100vh;
}

div.section {
width: 100%;

&.highlight {
background-color: var(--color-background-tertiary);
}
}

div.name-header {
text-transform: lowercase;
margin: 1rem auto;
text-align: center;
// text-align: center;

h1 { margin: 0 }
h4 {
Expand Down Expand Up @@ -76,9 +84,12 @@ div.gallery {
nav.main-nav {
@include flex-row();
justify-content: space-evenly;
padding-bottom: 1rem;
column-gap: 1rem;
width: 100%
width: 100%;

a {
font-weight: bold;
}
}

footer {
Expand All @@ -91,6 +102,10 @@ footer {
}
}

div#git-hash {
font-family: monospace;
}

blockquote {
border-left: 4px solid var(--color-accent);
margin-left: 0;
Expand Down Expand Up @@ -148,13 +163,15 @@ div.image-info, div.post-info {
}
}

p.image-time, p.post-time {
td.image-time, td.post-time {
margin: 0;
font-size: 0.75rem;
font-weight: bold;

svg {
> svg {
margin-right: 0.25em;
width: 1rem;
fill: var(--color-text-secondary);
}
}
}
Expand Down Expand Up @@ -240,4 +257,8 @@ div.home-container {
column-gap: 1rem;
}
}
}

.secondary-font {
font-family: monospace;
}
Loading