summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--Makefile7
-rw-r--r--alabaster-lite/config.html4
-rw-r--r--alabaster-lite/css/alabaster.css124
-rw-r--r--alabaster-lite/css/fkfd.css360
-rw-r--r--alabaster-lite/main.html11
-rw-r--r--alabaster-lite/sidebars/links.html47
-rw-r--r--alabaster-lite/sidebars/title.html (renamed from alabaster-lite/sidebars/about.html)5
-rwxr-xr-xatom/atom.py136
-rwxr-xr-xatom/scp2
-rw-r--r--docs/about.md81
-rw-r--r--docs/ham/img/wartime/7300.jpgbin0 -> 25834 bytes
-rw-r--r--docs/ham/img/wartime/ft5dr.jpgbin0 -> 23162 bytes
-rw-r--r--docs/ham/img/wartime/ts890s.jpgbin0 -> 35264 bytes
-rw-r--r--docs/ham/img/wartime/uv5r.jpgbin0 -> 50754 bytes
-rw-r--r--docs/ham/index.md17
-rw-r--r--docs/ham/joke_cq.md47
-rw-r--r--docs/ham/wartime.md103
-rw-r--r--docs/img/icons/about.pngbin0 -> 7898 bytes
-rw-r--r--docs/img/icons/comics-hover.pngbin0 -> 8962 bytes
-rw-r--r--docs/img/icons/comics.pngbin0 -> 6613 bytes
-rw-r--r--docs/img/icons/fediring.krabin0 -> 174002 bytes
-rw-r--r--docs/img/icons/fediring.pngbin0 -> 11426 bytes
-rw-r--r--docs/img/icons/fediring_next.pngbin0 -> 6287 bytes
-rw-r--r--docs/img/icons/fediring_prev.pngbin0 -> 5493 bytes
-rw-r--r--docs/img/icons/feed.pngbin0 -> 13079 bytes
-rw-r--r--docs/img/icons/ham-hover.pngbin0 -> 12652 bytes
-rw-r--r--docs/img/icons/ham.pngbin0 -> 11351 bytes
-rw-r--r--docs/img/icons/icons.krabin0 -> 855528 bytes
-rw-r--r--docs/img/icons/links.pngbin0 -> 10202 bytes
-rw-r--r--docs/img/icons/music-hover.pngbin0 -> 14611 bytes
-rw-r--r--docs/img/icons/music.pngbin0 -> 14527 bytes
-rw-r--r--docs/img/icons/projects-hover.pngbin0 -> 10833 bytes
-rw-r--r--docs/img/icons/projects.pngbin0 -> 9362 bytes
-rw-r--r--docs/img/icons/random-hover.pngbin0 -> 14085 bytes
-rw-r--r--docs/img/icons/random.pngbin0 -> 12956 bytes
-rw-r--r--docs/img/icons/shitpost-hover.pngbin0 -> 10236 bytes
-rw-r--r--docs/img/icons/shitpost.pngbin0 -> 7398 bytes
-rw-r--r--docs/img/icons/ta-hover.pngbin0 -> 9997 bytes
-rw-r--r--docs/img/icons/ta.pngbin0 -> 9308 bytes
-rw-r--r--docs/img/icons/umich-hover.pngbin0 -> 15296 bytes
-rw-r--r--docs/img/icons/umich.pngbin0 -> 10085 bytes
-rw-r--r--docs/img/icons/x.pngbin0 -> 7438 bytes
-rw-r--r--docs/index.md104
-rw-r--r--docs/links.md66
-rw-r--r--docs/music/band_discovery.md328
-rw-r--r--docs/music/clancy_review.md111
-rw-r--r--docs/music/deathbed_playlist.md17
-rw-r--r--docs/music/early-sunsets-over-monroeville.md152
-rw-r--r--docs/music/img/band_discovery/band.krabin0 -> 164631 bytes
-rw-r--r--docs/music/img/band_discovery/curve.pngbin0 -> 45930 bytes
-rw-r--r--docs/music/img/clancy_review/clancy.jpgbin0 -> 222620 bytes
-rw-r--r--docs/music/img/clancy_review/overcompensate_thumbnail.jpgbin0 -> 133296 bytes
-rw-r--r--docs/music/img/clancy_review/watch.jpgbin0 -> 158751 bytes
-rw-r--r--docs/music/img/early_sunsets_over_monroeville/editor.pngbin0 -> 343604 bytes
-rw-r--r--docs/music/img/early_sunsets_over_monroeville/export_analysis.pngbin0 -> 871418 bytes
-rw-r--r--docs/music/img/early_sunsets_over_monroeville/intro_strum.oggbin0 -> 153409 bytes
-rw-r--r--docs/music/img/early_sunsets_over_monroeville/mic_oscillations.pngbin0 -> 17512 bytes
-rw-r--r--docs/music/img/early_sunsets_over_monroeville/mixer.pngbin0 -> 229432 bytes
-rw-r--r--docs/music/img/early_sunsets_over_monroeville/prechorus_lead.oggbin0 -> 329006 bytes
-rw-r--r--docs/music/img/early_sunsets_over_monroeville/prechorus_plucks.oggbin0 -> 188670 bytes
-rw-r--r--docs/music/img/index/ibanez.jpgbin0 -> 92441 bytes
-rw-r--r--docs/music/img/index/squier.jpgbin0 -> 252303 bytes
-rw-r--r--docs/music/img/uncure-your-depression/album-cover.jpgbin0 -> 204546 bytes
-rw-r--r--docs/music/img/uncure-your-depression/ardour-editor.pngbin0 -> 561969 bytes
-rw-r--r--docs/music/img/uncure-your-depression/bass-chorus1.oggbin0 -> 78667 bytes
-rw-r--r--docs/music/img/uncure-your-depression/bass-chorus2.oggbin0 -> 77217 bytes
-rw-r--r--docs/music/img/uncure-your-depression/bass-solo.oggbin0 -> 74335 bytes
-rw-r--r--docs/music/img/uncure-your-depression/fuzz-guitar.oggbin0 -> 206411 bytes
-rw-r--r--docs/music/img/uncure-your-depression/lmms-editor.pngbin0 -> 180390 bytes
-rw-r--r--docs/music/img/uncure-your-depression/lmms.oggbin0 -> 2219163 bytes
-rw-r--r--docs/music/img/uncure-your-depression/looptober-v1.oggbin0 -> 1001268 bytes
-rw-r--r--docs/music/img/uncure-your-depression/looptober-v2.oggbin0 -> 1728610 bytes
-rw-r--r--docs/music/img/uncure-your-depression/solo-bassline.oggbin0 -> 216112 bytes
-rw-r--r--docs/music/img/uncure-your-depression/tswuyd-20231031.oggbin0 -> 2748139 bytes
-rw-r--r--docs/music/index.md101
-rw-r--r--docs/music/mcr_discog_review.md138
-rw-r--r--docs/music/uncure-your-depression.md323
-rw-r--r--docs/projects/bash-workshop.md340
-rw-r--r--docs/projects/bikeblinkers.md2
-rw-r--r--docs/projects/blobcat-pcb.md171
-rw-r--r--docs/projects/byseekel.md380
-rw-r--r--docs/projects/byseekel_scrapped.md441
-rw-r--r--docs/projects/fkfdme.md191
-rw-r--r--docs/projects/idkhow-pcb.md91
-rw-r--r--docs/projects/img/bash-workshop/average_familiarity.pngbin0 -> 35986 bytes
-rw-r--r--docs/projects/img/bash-workshop/bash_history.pngbin0 -> 58917 bytes
-rw-r--r--docs/projects/img/bash-workshop/cheatsheet.pngbin0 -> 79696 bytes
-rw-r--r--docs/projects/img/bash-workshop/cli_challenge.pngbin0 -> 54397 bytes
-rw-r--r--docs/projects/img/bash-workshop/environment_variables.pngbin0 -> 72233 bytes
-rw-r--r--docs/projects/img/bash-workshop/navbar.pngbin0 -> 2748 bytes
-rw-r--r--docs/projects/img/bash-workshop/rickroll.pngbin0 -> 165868 bytes
-rw-r--r--docs/projects/img/bash-workshop/ultimate_challenge.pngbin0 -> 68703 bytes
-rw-r--r--docs/projects/img/bash-workshop/wechat.pngbin0 -> 67742 bytes
-rw-r--r--docs/projects/img/bash-workshop/workshop.jpgbin0 -> 1715267 bytes
-rw-r--r--docs/projects/img/blobcat-pcb/logic.pngbin0 -> 105635 bytes
-rw-r--r--docs/projects/img/blobcat-pcb/marquee.gifbin0 -> 721912 bytes
-rw-r--r--docs/projects/img/blobcat-pcb/outline.pngbin0 -> 19546 bytes
-rw-r--r--docs/projects/img/blobcat-pcb/v0.2-cu.pngbin0 -> 175618 bytes
-rw-r--r--docs/projects/img/blobcat-pcb/v0.4-cu.pngbin0 -> 120132 bytes
-rw-r--r--docs/projects/img/byseekel/assembly.jpgbin0 -> 322766 bytes
-rw-r--r--docs/projects/img/byseekel/basket_mount.jpgbin0 -> 96146 bytes
-rw-r--r--docs/projects/img/byseekel/bikeblinkers.jpgbin0 -> 70505 bytes
-rw-r--r--docs/projects/img/byseekel/brakes_idea.pngbin0 -> 152797 bytes
-rw-r--r--docs/projects/img/byseekel/buzzer.jpgbin0 -> 85490 bytes
-rw-r--r--docs/projects/img/byseekel/buzzer_drill.jpgbin0 -> 62649 bytes
-rw-r--r--docs/projects/img/byseekel/feature_creep.pngbin0 -> 97448 bytes
-rw-r--r--docs/projects/img/byseekel/feature_creep_removal.pngbin0 -> 122732 bytes
-rw-r--r--docs/projects/img/byseekel/led_strip.jpgbin0 -> 84664 bytes
-rw-r--r--docs/projects/img/byseekel/panel_burnt.jpgbin0 -> 141283 bytes
-rw-r--r--docs/projects/img/byseekel/panel_freecad.pngbin0 -> 428031 bytes
-rw-r--r--docs/projects/img/byseekel/panel_laser.pngbin0 -> 37310 bytes
-rw-r--r--docs/projects/img/byseekel/rev0.jpgbin0 -> 184214 bytes
-rw-r--r--docs/projects/img/byseekel/rev0_back.jpgbin0 -> 301843 bytes
-rw-r--r--docs/projects/img/byseekel/rev1.jpgbin0 -> 154480 bytes
-rw-r--r--docs/projects/img/byseekel/rev2.jpgbin0 -> 107289 bytes
-rw-r--r--docs/projects/img/byseekel/rev2_jdb.pngbin0 -> 128766 bytes
-rw-r--r--docs/projects/img/byseekel/rev3.jpgbin0 -> 240949 bytes
-rw-r--r--docs/projects/img/byseekel/stalks.jpgbin0 -> 113355 bytes
-rw-r--r--docs/projects/img/byseekel/wtf.mp4bin0 -> 4842702 bytes
-rw-r--r--docs/projects/img/fkfdme/2020-04-23.pngbin0 -> 165483 bytes
-rw-r--r--docs/projects/img/fkfdme/2022-01-07.pngbin0 -> 40093 bytes
-rw-r--r--docs/projects/img/fkfdme/2022-01-09.pngbin0 -> 140287 bytes
-rw-r--r--docs/projects/img/fkfdme/2022-04-11.pngbin0 -> 132432 bytes
-rw-r--r--docs/projects/img/fkfdme/2022-05-07.pngbin0 -> 126390 bytes
-rw-r--r--docs/projects/img/fkfdme/2022-12-28.pngbin0 -> 190342 bytes
-rw-r--r--docs/projects/img/fkfdme/2023-01-15.pngbin0 -> 173981 bytes
-rw-r--r--docs/projects/img/fkfdme/2023-01-17.pngbin0 -> 187447 bytes
-rw-r--r--docs/projects/img/fkfdme/2023-09-03.pngbin0 -> 224296 bytes
-rw-r--r--docs/projects/img/fkfdme/2023-11-05.pngbin0 -> 213628 bytes
-rw-r--r--docs/projects/img/fkfdme/2023-11-27.pngbin0 -> 185333 bytes
-rw-r--r--docs/projects/img/fkfdme/icons.pngbin0 -> 160351 bytes
-rw-r--r--docs/projects/img/idkhow-pcb/bantam.jpgbin0 -> 338891 bytes
-rw-r--r--docs/projects/img/idkhow-pcb/kicad_pcb.pngbin0 -> 93357 bytes
-rw-r--r--docs/projects/img/idkhow-pcb/product.jpgbin0 -> 308565 bytes
-rw-r--r--docs/projects/img/idkhow-pcb/stack.jpgbin0 -> 151479 bytes
-rw-r--r--docs/projects/img/nand2tetris_1/6502.jpgbin0 -> 235588 bytes
-rw-r--r--docs/projects/img/nand2tetris_1/alu.krabin0 -> 507037 bytes
-rw-r--r--docs/projects/img/nand2tetris_1/alu.pngbin0 -> 122130 bytes
-rw-r--r--docs/projects/img/nand2tetris_1/alu_highlighted.pngbin0 -> 93144 bytes
-rw-r--r--docs/projects/img/nand2tetris_1/and_gate.pngbin0 -> 11081 bytes
-rw-r--r--docs/projects/img/nand2tetris_1/and_gate_nand.pngbin0 -> 17995 bytes
-rw-r--r--docs/projects/img/nand2tetris_1/and_gate_nand_not.pngbin0 -> 16708 bytes
-rw-r--r--docs/projects/img/nand2tetris_1/computer.krabin0 -> 460149 bytes
-rw-r--r--docs/projects/img/nand2tetris_1/computer_registers.pngbin0 -> 143235 bytes
-rw-r--r--docs/projects/img/nand2tetris_1/cpu.krabin0 -> 841631 bytes
-rw-r--r--docs/projects/img/nand2tetris_1/cpu.pngbin0 -> 222785 bytes
-rw-r--r--docs/projects/img/nand2tetris_1/cpu_book.pngbin0 -> 170476 bytes
-rw-r--r--docs/projects/img/nand2tetris_1/cpu_controls.pngbin0 -> 196576 bytes
-rw-r--r--docs/projects/img/nand2tetris_1/cpu_emulator_pong.pngbin0 -> 49719 bytes
-rw-r--r--docs/projects/img/nand2tetris_1/cpu_hunch.pngbin0 -> 177527 bytes
-rw-r--r--docs/projects/img/nand2tetris_1/cpu_mysterious_jump_logic.pngbin0 -> 227952 bytes
-rw-r--r--docs/projects/img/nand2tetris_1/mux.pngbin0 -> 27133 bytes
-rw-r--r--docs/projects/img/nand2tetris_1/pc.pngbin0 -> 84490 bytes
-rw-r--r--docs/projects/img/nand2tetris_1/pc_incorrect.pngbin0 -> 83727 bytes
-rw-r--r--docs/projects/img/nand2tetris_1/register.pngbin0 -> 15090 bytes
-rw-r--r--docs/projects/img/nand2tetris_1/register_internal.pngbin0 -> 26004 bytes
-rw-r--r--docs/projects/img/nand2tetris_1/zx_nx.pngbin0 -> 26921 bytes
-rw-r--r--docs/projects/img/nand2tetris_2.1/function_states.krabin0 -> 330831 bytes
-rw-r--r--docs/projects/img/nand2tetris_2.1/function_states.pngbin0 -> 57798 bytes
-rw-r--r--docs/projects/img/nand2tetris_2.1/function_states_blobs.pngbin0 -> 41665 bytes
-rw-r--r--docs/projects/img/nand2tetris_2.1/ram.krabin0 -> 745490 bytes
-rw-r--r--docs/projects/img/nand2tetris_2.1/ram_areas.pngbin0 -> 167613 bytes
-rw-r--r--docs/projects/img/nand2tetris_2.1/stack.krabin0 -> 1255273 bytes
-rw-r--r--docs/projects/img/nand2tetris_2.1/stack_after_add.pngbin0 -> 83319 bytes
-rw-r--r--docs/projects/img/nand2tetris_2.1/stack_after_call.pngbin0 -> 140659 bytes
-rw-r--r--docs/projects/img/nand2tetris_2.1/stack_after_function.pngbin0 -> 148639 bytes
-rw-r--r--docs/projects/img/nand2tetris_2.1/stack_after_return.pngbin0 -> 143907 bytes
-rw-r--r--docs/projects/img/nand2tetris_2.1/stack_before_add.pngbin0 -> 82498 bytes
-rw-r--r--docs/projects/img/nand2tetris_2.1/stack_before_call.pngbin0 -> 120212 bytes
-rw-r--r--docs/projects/img/nand2tetris_2.1/stack_before_restore.pngbin0 -> 157727 bytes
-rw-r--r--docs/projects/img/nand2tetris_2.1/stack_before_return.pngbin0 -> 154188 bytes
-rw-r--r--docs/projects/img/nand2tetris_2.1/stack_call_backup.pngbin0 -> 141559 bytes
-rw-r--r--docs/projects/img/nand2tetris_2.1/stack_push.pngbin0 -> 144641 bytes
-rw-r--r--docs/projects/img/reflow-workshop/anime.jpgbin0 -> 664448 bytes
-rw-r--r--docs/projects/img/reflow-workshop/avrdude.pngbin0 -> 17782 bytes
-rw-r--r--docs/projects/img/reflow-workshop/bicycle.jpgbin0 -> 104500 bytes
-rw-r--r--docs/projects/img/reflow-workshop/bolted_stencil.jpgbin0 -> 262126 bytes
-rw-r--r--docs/projects/img/reflow-workshop/cap_reflow.mp4bin0 -> 4256282 bytes
-rw-r--r--docs/projects/img/reflow-workshop/electronic_cat.jpgbin0 -> 75352 bytes
-rw-r--r--docs/projects/img/reflow-workshop/empty_tjy.jpgbin0 -> 89784 bytes
-rw-r--r--docs/projects/img/reflow-workshop/first_prototype.jpgbin0 -> 235724 bytes
-rw-r--r--docs/projects/img/reflow-workshop/flawed_qfn.jpgbin0 -> 173715 bytes
-rw-r--r--docs/projects/img/reflow-workshop/hotplate.jpgbin0 -> 129254 bytes
-rw-r--r--docs/projects/img/reflow-workshop/hotplate_sat.jpgbin0 -> 140540 bytes
-rw-r--r--docs/projects/img/reflow-workshop/japanspachtel.jpgbin0 -> 172124 bytes
-rw-r--r--docs/projects/img/reflow-workshop/keychain.jpgbin0 -> 144427 bytes
-rw-r--r--docs/projects/img/reflow-workshop/kliments_vs_mine.jpgbin0 -> 103194 bytes
-rw-r--r--docs/projects/img/reflow-workshop/partial_test_run_hotplate.jpgbin0 -> 228062 bytes
-rw-r--r--docs/projects/img/reflow-workshop/partial_test_run_joints.jpgbin0 -> 310719 bytes
-rw-r--r--docs/projects/img/reflow-workshop/partial_test_run_paste.jpgbin0 -> 238440 bytes
-rw-r--r--docs/projects/img/reflow-workshop/paste_best.jpgbin0 -> 168711 bytes
-rw-r--r--docs/projects/img/reflow-workshop/people_who.jpgbin0 -> 59105 bytes
-rw-r--r--docs/projects/img/reflow-workshop/perfect_qfn.jpgbin0 -> 76798 bytes
-rw-r--r--docs/projects/img/reflow-workshop/pick_and_place.jpgbin0 -> 213210 bytes
-rw-r--r--docs/projects/img/reflow-workshop/poster.pngbin0 -> 2140360 bytes
-rw-r--r--docs/projects/img/reflow-workshop/poster_printing.jpgbin0 -> 200091 bytes
-rw-r--r--docs/projects/img/reflow-workshop/product_sat.jpgbin0 -> 159070 bytes
-rw-r--r--docs/projects/img/reflow-workshop/qfn32_joint_short.jpgbin0 -> 66982 bytes
-rw-r--r--docs/projects/img/reflow-workshop/qfn32_paste_sat.jpgbin0 -> 399508 bytes
-rw-r--r--docs/projects/img/reflow-workshop/qfn32_paste_short.jpgbin0 -> 93449 bytes
-rw-r--r--docs/projects/img/reflow-workshop/qfn32_paste_success.jpgbin0 -> 186739 bytes
-rw-r--r--docs/projects/img/reflow-workshop/qfn32_paste_sun.jpgbin0 -> 402023 bytes
-rw-r--r--docs/projects/img/reflow-workshop/qfn32_socket.jpgbin0 -> 64600 bytes
-rw-r--r--docs/projects/img/reflow-workshop/qfn32_socket_loaded.jpgbin0 -> 221351 bytes
-rw-r--r--docs/projects/img/reflow-workshop/reflow_workshop_title.pngbin0 -> 45677 bytes
-rw-r--r--docs/projects/img/reflow-workshop/solder_paste_scraped.jpgbin0 -> 221316 bytes
-rw-r--r--docs/projects/img/reflow-workshop/stencils.jpgbin0 -> 112299 bytes
-rw-r--r--docs/projects/img/reflow-workshop/trash.jpgbin0 -> 144800 bytes
-rw-r--r--docs/projects/img/reflow-workshop/usbasp.jpgbin0 -> 136407 bytes
-rw-r--r--docs/projects/img/sirtet/demo.mp4bin0 -> 258166 bytes
-rw-r--r--docs/projects/img/sirtet/sirtet.pngbin0 -> 28606 bytes
-rw-r--r--docs/projects/index.md84
-rw-r--r--docs/projects/kanvas.md29
-rw-r--r--docs/projects/nand2tetris_1.md945
-rw-r--r--docs/projects/nand2tetris_2.1.md554
-rw-r--r--docs/projects/nand2tetris_2.2.md178
-rw-r--r--docs/projects/reflow-workshop.md792
-rw-r--r--docs/projects/rickstodon.md6
-rw-r--r--docs/projects/sirtet.md248
-rw-r--r--docs/random/2024-04-05.md407
-rw-r--r--docs/random/2bugs1day.md117
-rw-r--r--docs/random/ergo_m575.md88
-rw-r--r--docs/random/how_fkfd_is_made.md115
-rw-r--r--docs/random/i_respect_furries.md38
-rw-r--r--docs/random/img/2bugs1day/re.pngbin0 -> 26941 bytes
-rw-r--r--docs/random/img/ergo_m575/bottom.jpgbin0 -> 642643 bytes
-rw-r--r--docs/random/img/ergo_m575/left.jpgbin0 -> 879822 bytes
-rw-r--r--docs/random/img/ergo_m575/rear.jpgbin0 -> 797835 bytes
-rw-r--r--docs/random/img/ergo_m575/right.jpgbin0 -> 630092 bytes
-rw-r--r--docs/random/img/ergo_m575/socket.jpgbin0 -> 520713 bytes
-rw-r--r--docs/random/img/ergo_m575/top.jpgbin0 -> 549192 bytes
-rw-r--r--docs/random/img/ergo_m575/trackball.jpgbin0 -> 910940 bytes
-rw-r--r--docs/random/img/latex_handwriting/linalg_notes.pngbin0 -> 127790 bytes
-rw-r--r--docs/random/img/latex_handwriting/long_equation.pngbin0 -> 289146 bytes
-rw-r--r--docs/random/img/shenzhen_io_review/canvas.pngbin0 -> 697774 bytes
-rw-r--r--docs/random/img/shenzhen_io_review/sandwich_machine.pngbin0 -> 568237 bytes
-rw-r--r--docs/random/img/shenzhen_io_review/shoes.pngbin0 -> 712891 bytes
-rw-r--r--docs/random/img/shenzhen_io_review/shoes_notch.pngbin0 -> 43674 bytes
-rw-r--r--docs/random/img/shenzhen_io_review/shoes_wires.pngbin0 -> 471521 bytes
-rw-r--r--docs/random/img/shenzhen_io_review/traffic_signal.pngbin0 -> 750846 bytes
-rw-r--r--docs/random/img/shenzhen_io_review/traffic_signal_histograms.pngbin0 -> 252769 bytes
-rw-r--r--docs/random/img/smartphone_os/miui_open_app_popup.png (renamed from docs/random/img/miui_open_app_popup.png)bin97184 -> 97184 bytes
-rw-r--r--docs/random/img/smartphone_os/o2os11_power_menu.png (renamed from docs/random/img/o2os11_power_menu.png)bin78684 -> 78684 bytes
-rw-r--r--docs/random/img/smartphone_os/o2os12_power_menu.png (renamed from docs/random/img/o2os12_power_menu.png)bin65736 -> 65736 bytes
-rw-r--r--docs/random/img/tab_gang/braille_display.jpgbin0 -> 88717 bytes
-rw-r--r--docs/random/img/xkcdbot/post.pngbin0 -> 158913 bytes
-rw-r--r--docs/random/img/xkcdbot/thumbnail.pngbin0 -> 76348 bytes
-rw-r--r--docs/random/index.md19
-rw-r--r--docs/random/latex_handwriting.md78
-rw-r--r--docs/random/life_goals.md78
-rw-r--r--docs/random/potato_chips.md243
-rw-r--r--docs/random/shenzhen_io_review.md268
-rw-r--r--docs/random/smartphone_os.md6
-rw-r--r--docs/random/tab_gang.md44
-rw-r--r--docs/random/xkcdbot.md152
-rw-r--r--docs/shitpost/cringiest_song.md43
-rw-r--r--docs/shitpost/haiku_2.md34
-rw-r--r--docs/shitpost/index.md13
-rw-r--r--docs/shitpost/no_backspace.md36
-rw-r--r--docs/ta/img/vg151_e1/graph_color.pngbin0 -> 1138 bytes
-rw-r--r--docs/ta/img/vg151_e1/graph_hsl.pngbin0 -> 1882 bytes
-rw-r--r--docs/ta/img/vg151_e1/graph_hsv.pngbin0 -> 1684 bytes
-rw-r--r--docs/ta/img/vg151_e1/graph_printed.pngbin0 -> 3495 bytes
-rw-r--r--docs/ta/img/vg151_e1/sierpinski.pngbin0 -> 17681 bytes
-rw-r--r--docs/ta/img/vg151_funny_shit/deleted_quiz.pngbin0 -> 27268 bytes
-rw-r--r--docs/ta/img/vg151_funny_shit/jiang_avi.pngbin0 -> 9668 bytes
-rw-r--r--docs/ta/img/vg151_funny_shit/minamike.pngbin0 -> 712877 bytes
-rw-r--r--docs/ta/img/vg151_funny_shit/mm_emoticon.pngbin0 -> 40433 bytes
-rw-r--r--docs/ta/img/vg151_funny_shit/moss_example.pngbin0 -> 25640 bytes
-rw-r--r--docs/ta/img/vg151_funny_shit/moss_reenactment.pngbin0 -> 9834 bytes
-rw-r--r--docs/ta/img/vg151_funny_shit/nuclear_acid.pngbin0 -> 10705 bytes
-rw-r--r--docs/ta/img/vg151_funny_shit/p2_code.pngbin0 -> 197649 bytes
-rw-r--r--docs/ta/img/vg151_funny_shit/ssh-keygen.pngbin0 -> 56450 bytes
-rw-r--r--docs/ta/img/vg151_funny_shit/target_os_iphone.pngbin0 -> 19773 bytes
-rw-r--r--docs/ta/img/vg151_lab1/lab1_dont_understand.pngbin0 -> 44311 bytes
-rw-r--r--docs/ta/img/vg151_lab1/lab1_rc.pngbin0 -> 8237 bytes
-rw-r--r--docs/ta/index.md42
-rw-r--r--docs/ta/vg151_e1.md455
-rw-r--r--docs/ta/vg151_funny_shit.md285
-rw-r--r--docs/ta/vg151_lab1.md142
-rw-r--r--docs/umich/f23_winter_break.md98
-rw-r--r--docs/umich/f23_wrapup.md456
-rw-r--r--docs/umich/img/f23_winter_break/broccoli_goo.jpgbin0 -> 122324 bytes
-rw-r--r--docs/umich/img/f23_winter_break/broccoli_soup.jpgbin0 -> 118798 bytes
-rw-r--r--docs/umich/img/f23_winter_break/broken_pipe.jpgbin0 -> 90483 bytes
-rw-r--r--docs/umich/img/f23_winter_break/bunny.jpgbin0 -> 24667 bytes
-rw-r--r--docs/umich/img/f23_winter_break/mushroom_soup.jpgbin0 -> 102477 bytes
-rw-r--r--docs/umich/img/f23_wrapup/badapple.pngbin0 -> 292407 bytes
-rw-r--r--docs/umich/img/f23_wrapup/tigerente.jpgbin0 -> 195105 bytes
-rw-r--r--docs/umich/index.md7
-rw-r--r--docs/umich/us_lessons.md115
-rw-r--r--mkdocs.yml11
293 files changed, 9895 insertions, 234 deletions
diff --git a/.gitignore b/.gitignore
index c32c618..35038e9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,7 @@
site/
.undodir/
+.directory
docs/*/img/*/fullsize/
wip/
+*~
+atom.xml
diff --git a/Makefile b/Makefile
index ec201ae..989ee67 100644
--- a/Makefile
+++ b/Makefile
@@ -1,10 +1,13 @@
-all: build deploy
+all: build deploy push
build:
mkdocs build
preview:
- mkdocs serve
+ mkdocs serve & sleep 1 && xdg-open "http://localhost:8000"
deploy:
rsync -rv --delete ./site/* www@fkfd.me:www/blog/
+
+push:
+ git push
diff --git a/alabaster-lite/config.html b/alabaster-lite/config.html
index 51f1871..10fc18a 100644
--- a/alabaster-lite/config.html
+++ b/alabaster-lite/config.html
@@ -7,7 +7,7 @@
"include_toc": e.include_toc | default(true),
"extra_nav_links": e.extra_nav_links | default({}),
"show_powered_by": e.show_powered_by | default(true),
- "sidebars": e.sidebars | default(["about", "toc"]),
+ "sidebars": e.sidebars | default(["title", "toc"]),
"homepage_nav": e.homepage_nav | default(true),
- "homepage_sidebars": e.homepage_sidebars | default(["about"]),
+ "homepage_sidebars": e.homepage_sidebars | default(["title", "about"]),
}%}
diff --git a/alabaster-lite/css/alabaster.css b/alabaster-lite/css/alabaster.css
index 89eae81..f5ad007 100644
--- a/alabaster-lite/css/alabaster.css
+++ b/alabaster-lite/css/alabaster.css
@@ -1,12 +1,8 @@
-/* This file used to import a css called basic.css, but the latter is fused into L584 now */
-
/* -- page layout ----------------------------------------------------------- */
body {
font-family: "Noto Sans", Roboto, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
font-size: 18px;
- background-color: white;
- color: #000;
margin: 0;
padding: 0;
}
@@ -34,8 +30,6 @@ hr {
}
div.body {
- background-color: #ffffff;
- color: #3E4349;
padding: 0 30px 0 30px;
}
@@ -60,9 +54,7 @@ div.relations {
display: none;
}
-
div.sphinxsidebar a {
- color: #444;
text-decoration: none;
border-bottom: 1px dotted #999;
}
@@ -105,7 +97,6 @@ div.sphinxsidebarwrapper p.blurb {
div.sphinxsidebar h3,
div.sphinxsidebar h4 {
- color: #444;
font-size: 24px;
font-weight: normal;
margin: 0 0 5px 0;
@@ -116,10 +107,6 @@ div.sphinxsidebar h4 {
font-size: 20px;
}
-div.sphinxsidebar h3 a {
- color: #444;
-}
-
div.sphinxsidebar p.logo a,
div.sphinxsidebar h3 a,
div.sphinxsidebar p.logo a:hover,
@@ -128,14 +115,12 @@ div.sphinxsidebar h3 a:hover {
}
div.sphinxsidebar p {
- color: #555;
margin: 10px 0;
}
div.sphinxsidebar ul {
margin: 10px 0;
padding: 0;
- color: #000;
}
div.sphinxsidebar ul li.toctree-l1 > a {
@@ -165,16 +150,6 @@ div.sphinxsidebar hr {
/* -- body styles ----------------------------------------------------------- */
-a {
- color: #004B6B;
- text-decoration: underline;
-}
-
-a:hover {
- color: #206B8B;
- text-decoration: underline;
-}
-
div.body h1,
div.body h2,
div.body h3,
@@ -194,6 +169,10 @@ div.body h4 { font-size: 130%; }
div.body h5 { font-size: 100%; }
div.body h6 { font-size: 100%; }
+a, a:hover {
+ text-decoration: underline;
+}
+
a.headerlink {
color: #DDD;
padding: 0 4px;
@@ -367,7 +346,6 @@ ul, ol {
}
pre {
- background: #EEE;
padding: 7px 30px;
margin: 15px 0px;
line-height: 1.3em;
@@ -383,12 +361,6 @@ dl dl pre {
padding-left: 90px;
}
-tt, code {
- background-color: #EEE;
- color: #222;
- /* padding: 1px 2px; */
-}
-
tt.xref, code.xref, a tt {
background-color: #FBFBFB;
border-bottom: 1px solid white;
@@ -414,14 +386,9 @@ a.footnote-reference:hover {
border-bottom: 1px solid #6D4100;
}
-a:hover tt, a:hover code {
- background: #EEE;
-}
-
@media screen and (max-width: 870px) {
-
div.sphinxsidebar {
- display: none;
+ display: none;
}
div.document {
@@ -429,25 +396,25 @@ a:hover tt, a:hover code {
}
div.documentwrapper {
- margin-left: 0;
- margin-top: 0;
- margin-right: 0;
- margin-bottom: 0;
+ margin-left: 0;
+ margin-top: 0;
+ margin-right: 0;
+ margin-bottom: 0;
}
div.bodywrapper {
- margin-top: 0;
- margin-right: 0;
- margin-bottom: 0;
- margin-left: 0;
+ margin-top: 0;
+ margin-right: 0;
+ margin-bottom: 0;
+ margin-left: 0;
}
ul {
- margin-left: 0;
+ margin-left: 0;
}
.document {
- width: auto;
+ width: auto;
}
.footer {
@@ -455,25 +422,19 @@ a:hover tt, a:hover code {
}
.bodywrapper {
- margin: 0;
+ margin: 0;
}
.footer {
- width: auto;
+ width: auto;
}
.github {
display: none;
}
-
-
-
}
-
-
@media screen and (max-width: 875px) {
-
body {
margin: 0;
padding: 20px 30px;
@@ -542,7 +503,6 @@ a:hover tt, a:hover code {
}
}
-
/* misc. */
.revsys-inline {
@@ -1017,7 +977,6 @@ dl.glossary dt {
}
abbr, acronym {
- border-bottom: dotted 1px;
cursor: help;
}
@@ -1133,52 +1092,3 @@ span.eqno {
display: none;
}
}
-
-@media (prefers-color-scheme: dark) {
- body {
- color: white;
- background-color: #19202b;
- }
-
- div.body {
- color: white;
- background-color: #19202b;
- padding-bottom: 24px;
- }
-
- div.sphinxsidebar h3,
- div.sphinxsidebar h3 a,
- div.sphinxsidebar h4,
- div.sphinxsidebar p,
- div.sphinxsidebar a,
- div.sphinxsidebar ul {
- color: #bbb;
- }
-
- a {
- color: #7b91be;
- }
-
- a:hover {
- color: #9bb1de;
- }
-
- tt, code {
- background-color: #202836;
- color: white;
- }
-
- a:hover tt, a:hover code {
- background: #666;
- }
-
- pre {
- background-color: #202836;
- border: 1px solid white;
- border-radius: 8px;
- }
-}
-
-video {
- max-width: 100%;
-}
diff --git a/alabaster-lite/css/fkfd.css b/alabaster-lite/css/fkfd.css
new file mode 100644
index 0000000..83e8715
--- /dev/null
+++ b/alabaster-lite/css/fkfd.css
@@ -0,0 +1,360 @@
+/*
+ * Light/dark color schemes for fkfd.me
+ */
+
+body {
+ background-color: white;
+ color: #000;
+}
+
+div.body {
+ background-color: #ffffff;
+ color: #3E4349;
+}
+
+@media (prefers-color-scheme: dark) {
+ body {
+ color: white;
+ background-color: #19202b;
+ }
+
+ div.body {
+ color: white;
+ background-color: #19202b;
+ padding-bottom: 24px;
+ }
+}
+
+a {
+ color: #004b6b;
+}
+
+a:hover {
+ color: #206b8b;
+}
+
+summary {
+ color: #004b6b;
+}
+
+@media (prefers-color-scheme: dark) {
+ a {
+ color: #a5c5ff;
+ }
+
+ a:hover {
+ color: #cddfff;
+ }
+
+ summary {
+ color: #a5c5ff;
+ }
+}
+
+a.headerlink {
+ color: #004b6b;
+ margin-left: 0.5em;
+}
+
+a.headerlink:hover {
+ color: #206b8b;
+ background: none;
+}
+
+@media (prefers-color-scheme: dark) {
+ a.headerlink {
+ color: #a5c5ff;
+ }
+
+ a.headerlink:hover {
+ color: #cddfff;
+ }
+}
+
+div.sphinxsidebar h3,
+div.sphinxsidebar h3 a,
+div.sphinxsidebar h4,
+div.sphinxsidebar a {
+ color: #444;
+}
+
+div.sphinxsidebar p {
+ color: #555;
+}
+
+div.sphinxsidebar ul {
+ color: #000;
+}
+
+@media (prefers-color-scheme: dark) {
+ div.sphinxsidebar h3,
+ div.sphinxsidebar h3 a,
+ div.sphinxsidebar h4,
+ div.sphinxsidebar p,
+ div.sphinxsidebar a,
+ div.sphinxsidebar ul {
+ color: #bbb;
+ }
+}
+
+tt, code {
+ background-color: #EEE;
+ color: #222;
+}
+
+a:hover tt, a:hover code {
+ background: #EEE;
+}
+
+pre {
+ background: #EEE;
+}
+
+@media (prefers-color-scheme: dark) {
+ tt, code {
+ background-color: #202836;
+ color: white;
+ }
+
+ a:hover tt, a:hover code {
+ background: #666;
+ }
+
+ pre {
+ background-color: #202836;
+ border: 1px solid white;
+ border-radius: 8px;
+ }
+}
+
+video {
+ max-width: 100%;
+}
+
+div.footer,
+div.footer a {
+ color: #3E4349;
+}
+
+@media (prefers-color-scheme: dark) {
+ div.footer,
+ div.footer a {
+ color: #eee;
+ }
+}
+
+@media screen and (max-width: 870px) {
+ div.sphinxsidebar {
+ float: none;
+ width: 100%;
+ margin: 120px -20px 20px;
+ padding: 0 20px;
+ background-color: transparent;
+ color: #ccc;
+ }
+}
+
+/*
+ * Custom home page for fkfd.me
+ * Created on 2022-12-28
+ * Last revision 2023-11-05
+ */
+
+.category-block-container {
+ display: grid;
+ gap: 20px;
+ grid-template-columns: 320px 320px;
+}
+
+.webring-block-container {
+ display: grid;
+ gap: 0px;
+ grid-template-columns: 60px 60px 60px;
+ padding-top: 20px;
+}
+
+.link-block-container {
+ display: grid;
+ gap: 20px;
+ grid-template-columns: 180px;
+}
+
+@media screen and (max-width: 870px) {
+ .category-block-container {
+ /* one column only on narrow screens */
+ grid-template-columns: 320px;
+ }
+}
+
+.webring-block-container a {
+ text-decoration: none;
+}
+
+.category-block, .webring-block, .link-block {
+ display: grid;
+ text-align: center;
+}
+
+.category-icon, .webring-icon, .link-icon {
+ display: block;
+ background-color: #3E4349;
+ background-size: contain;
+ background-repeat: no-repeat;
+ mask-size: contain;
+ mask-repeat: no-repeat;
+ -webkit-mask-size: contain;
+ -webkit-mask-repeat: no-repeat;
+}
+
+.category-icon {
+ width: 120px;
+ height: 120px;
+}
+
+.webring-icon {
+ width: 60px;
+ height: 60px;
+}
+
+.link-icon {
+ width: 60px;
+ height: 60px;
+}
+
+@media (prefers-color-scheme: dark) {
+ .category-icon, .webring-icon, .link-icon {
+ background-color: white;
+ }
+}
+
+.category-text {
+ position: absolute;
+ margin-left: 130px;
+ margin-top: 40px;
+ font-size: 1.5em;
+}
+
+.webring-text {
+ text-align: center;
+}
+
+.link-text {
+ position: absolute;
+ margin-left: 80px;
+ margin-top: 20px;
+}
+
+#icon-projects {
+ mask-image: url("../img/icons/projects.png");
+ -webkit-mask-image: url("../img/icons/projects.png");
+}
+
+#icon-random {
+ mask-image: url("../img/icons/random.png");
+ -webkit-mask-image: url("../img/icons/random.png");
+}
+
+#icon-shitpost {
+ mask-image: url("../img/icons/shitpost.png");
+ -webkit-mask-image: url("../img/icons/shitpost.png");
+}
+
+#icon-ta {
+ mask-image: url("../img/icons/ta.png");
+ -webkit-mask-image: url("../img/icons/ta.png");
+}
+
+#icon-ham {
+ mask-image: url("../img/icons/ham.png");
+ -webkit-mask-image: url("../img/icons/ham.png");
+}
+
+#icon-comics {
+ mask-image: url("../img/icons/comics.png");
+ -webkit-mask-image: url("../img/icons/comics.png");
+}
+
+#icon-music {
+ mask-image: url("../img/icons/music.png");
+ -webkit-mask-image: url("../img/icons/music.png");
+}
+
+#icon-umich {
+ mask-image: url("../img/icons/umich.png");
+ -webkit-mask-image: url("../img/icons/umich.png");
+}
+
+#icon-projects:hover {
+ mask-image: url("../img/icons/projects-hover.png");
+ -webkit-mask-image: url("../img/icons/projects-hover.png");
+}
+
+#icon-random:hover {
+ mask-image: url("../img/icons/random-hover.png");
+ -webkit-mask-image: url("../img/icons/random-hover.png");
+}
+
+#icon-shitpost:hover {
+ mask-image: url("../img/icons/shitpost-hover.png");
+ -webkit-mask-image: url("../img/icons/shitpost-hover.png");
+}
+
+#icon-ta:hover {
+ mask-image: url("../img/icons/ta-hover.png");
+ -webkit-mask-image: url("../img/icons/ta-hover.png");
+}
+
+#icon-ham:hover {
+ mask-image: url("../img/icons/ham-hover.png");
+ -webkit-mask-image: url("../img/icons/ham-hover.png");
+}
+
+#icon-comics:hover {
+ mask-image: url("../img/icons/comics-hover.png");
+ -webkit-mask-image: url("../img/icons/comics-hover.png");
+}
+
+#icon-music:hover {
+ mask-image: url("../img/icons/music-hover.png");
+ -webkit-mask-image: url("../img/icons/music-hover.png");
+}
+
+#icon-umich:hover {
+ mask-image: url("../img/icons/umich-hover.png");
+ -webkit-mask-image: url("../img/icons/umich-hover.png");
+}
+
+#icon-fediring-prev {
+ mask-image: url("../img/icons/fediring_prev.png");
+ -webkit-mask-image: url("../img/icons/fediring_prev.png");
+}
+
+#icon-fediring {
+ mask-image: url("../img/icons/fediring.png");
+ -webkit-mask-image: url("../img/icons/fediring.png");
+}
+
+#icon-fediring-next {
+ mask-image: url("../img/icons/fediring_next.png");
+ -webkit-mask-image: url("../img/icons/fediring_next.png");
+}
+
+#icon-about {
+ mask-image: url("../img/icons/about.png");
+ -webkit-mask-image: url("../img/icons/about.png");
+}
+
+#icon-feed {
+ mask-image: url("../img/icons/feed.png");
+ -webkit-mask-image: url("../img/icons/feed.png");
+}
+
+#icon-links {
+ mask-image: url("../img/icons/links.png");
+ -webkit-mask-image: url("../img/icons/links.png");
+}
+
+#icon-x {
+ mask-image: url("../img/icons/x.png");
+ -webkit-mask-image: url("../img/icons/x.png");
+}
diff --git a/alabaster-lite/main.html b/alabaster-lite/main.html
index 070adb7..ee56621 100644
--- a/alabaster-lite/main.html
+++ b/alabaster-lite/main.html
@@ -1,19 +1,22 @@
{% from "config.html" import theme with context %}
<!DOCTYPE html>
-<html>
+<html lang="en-US">
<head>
{% block head %}
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <title>{% if page.title %}{{ page.title }} &mdash; {% endif %}{{ config.site_name }}</title>
+ <title>
+ {% if page.title %} {{ page.title }} &mdash; {% endif %} {{ config.site_name }}
+ </title>
<link rel="stylesheet" href="{{ base_url }}/css/alabaster.css" type="text/css">
+ <link rel="stylesheet" href="{{ base_url }}/css/fkfd.css" type="text/css">
{% for path in extra_css %}
<link href="{{ path }}" rel="stylesheet">
{% endfor %}
- <meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9">
+ <meta name="viewport" content="width=device-width, initial-scale=0.9">
{% block extrahead %}{% endblock %}
@@ -60,7 +63,7 @@
{% endblock %}
{% if page.is_homepage %}
- <a hidden rel="me" href="https://mastodon.technology/@fakefred">Mastodon</a>
+ <a hidden rel="me" href="https://eattherich.club/@fkfd">Mastodon</a>
{% endif %}
<!--
diff --git a/alabaster-lite/sidebars/links.html b/alabaster-lite/sidebars/links.html
new file mode 100644
index 0000000..f5129ac
--- /dev/null
+++ b/alabaster-lite/sidebars/links.html
@@ -0,0 +1,47 @@
+<div class="link-block-container">
+ <a href="/about/">
+ <div class="link-block">
+ <span class="link-icon" id="icon-about"></span>
+ <span class="link-text">About Me</span>
+ </div>
+ </a>
+ <a href="/feed/atom.xml">
+ <div class="link-block">
+ <span class="link-icon" id="icon-feed"></span>
+ <span class="link-text">Atom Feed</span>
+ </div>
+ </a>
+ <a href="/links/">
+ <div class="link-block">
+ <span class="link-icon" id="icon-links"></span>
+ <span class="link-text">Links</span>
+ </div>
+ </a>
+ <a href="/x/">
+ <div class="link-block">
+ <span class="link-icon" id="icon-x"></span>
+ <span class="link-text">~x</span>
+ </div>
+ </a>
+</div>
+
+<div class="webring-block-container">
+ <a href="https://fediring.net/previous?host=fkfd.me">
+ <div class="webring-block">
+ <span class="webring-icon" id="icon-fediring-prev"></span>
+ <span class="webring-text">Prev</span>
+ </div>
+ </a>
+ <a href="https://fediring.net/">
+ <div class="webring-block">
+ <span class="webring-icon" id="icon-fediring"></span>
+ <span class="webring-text">Fediring</span>
+ </div>
+ </a>
+ <a href="https://fediring.net/next?host=fkfd.me">
+ <div class="webring-block">
+ <span class="webring-icon" id="icon-fediring-next"></span>
+ <span class="webring-text">Next</span>
+ </div>
+ </a>
+</div>
diff --git a/alabaster-lite/sidebars/about.html b/alabaster-lite/sidebars/title.html
index 674dab4..2bfd103 100644
--- a/alabaster-lite/sidebars/about.html
+++ b/alabaster-lite/sidebars/title.html
@@ -10,7 +10,4 @@
{% else %}
<h1 class="logo"><a href="{{nav.homepage.url|url}}">{{ config.site_name }}</a></h1>
{% endif %}
-
-{% if config.site_description %}
- <p class="blurb">{{ config.site_description }}</p>
-{% endif %}
+<p class="blurb">This is technically a blog.</p>
diff --git a/atom/atom.py b/atom/atom.py
new file mode 100755
index 0000000..b240378
--- /dev/null
+++ b/atom/atom.py
@@ -0,0 +1,136 @@
+#!/usr/bin/python
+import sys
+import json
+from urllib import parse as urlparse
+from bs4 import BeautifulSoup
+from datetime import datetime
+
+"""
+Unified Atom feed generator for fkfd.me and fkfd.me/comics.
+
+Each time an entry is added, the oldest entry is automatically purged when
+the list is at maximum capacity before atom.xml is generated.
+"""
+
+MAX_ENTRIES = 5
+
+COMICS_DIR = "/home/fkfd/p/fkfd"
+BLOG_DIR = "/home/fkfd/p/blog"
+
+
+def utc_date() -> str:
+ # UTC datetime: YYYY-MM-DDThh:mm:ssZ
+ return datetime.utcnow().replace(microsecond=0).isoformat() + "Z"
+
+
+def add_entry(entry: dict, dryrun=False):
+ f = open("atom.xml")
+ xml = f.read()
+ f.close()
+ soup = BeautifulSoup(xml, "xml")
+ entries = soup.find_all("entry")
+ if len(entries) >= MAX_ENTRIES:
+ # truncate to (MAX_ENTRIES - 1) entries to make room
+ for entry_to_remove in entries[(MAX_ENTRIES - 1) :]:
+ entry_to_remove.decompose()
+
+ new_entry = soup.new_tag("entry")
+ new_id = soup.new_tag("id")
+ new_id.string = entry["url"]
+ new_title = soup.new_tag("title")
+ new_title.string = entry["title"]
+ new_updated = soup.new_tag("updated")
+ new_updated.string = entry["date"]
+ new_content = soup.new_tag("content", type="html")
+ new_content.string = entry["html"]
+ new_link = soup.new_tag("link", href=entry["url"], rel="alternate")
+
+ for tag in [new_id, new_title, new_updated, new_content, new_link]:
+ new_entry.append(tag)
+
+ soup.find("entry").insert_before(new_entry)
+ soup.find("updated").string = utc_date()
+
+ if dryrun:
+ print(soup.prettify())
+ else:
+ f = open("atom.xml", "w")
+ f.write(str(soup))
+ f.close()
+
+
+def add_comic(num, dryrun=False):
+ try:
+ f = open(COMICS_DIR + f"/html/{num}/info.0.json")
+ except FileNotFoundError:
+ print(f"info.0.json for comic {num} not found. Skip.")
+ return
+
+ info = json.load(f)
+ f.close()
+
+ add_entry(
+ {
+ "url": f"https://fkfd.me/comics/{info['num']}",
+ "title": "Comic: " + info["title"],
+ "date": utc_date(),
+ "html": '<p><img src="{0}" alt="{1}"></p><p>{2}</p>'.format(
+ info["img"], info["transcript"], info["alt"]
+ ),
+ },
+ dryrun=dryrun,
+ )
+
+
+def add_blogpost(path, dryrun=False):
+ # example path: "shitpost/flat_egg", "projects/byseekel"
+ path = path.strip("/")
+ try:
+ f = open(BLOG_DIR + f"/site/{path}/index.html")
+ except FileNotFoundError:
+ print(f"index.html for blogpost {path} not found. Skip.")
+ return
+
+ html = f.read()
+ f.close()
+ soup = BeautifulSoup(html, "html.parser")
+ main = soup.find(role="main")
+ url = f"https://fkfd.me/{path}/" # trailing slash necessary
+
+ # convert all relative paths to absolute URLs
+ for img in main.find_all("img"):
+ img["src"] = urlparse.urljoin(url, img["src"])
+
+ for a in main.find_all("a"):
+ try:
+ if "headerlink" in a["class"]: # remove "¶"
+ a.decompose()
+ continue
+ a["href"] = urlparse.urljoin(url, a["href"])
+ except KeyError: # just a regular link, no class attribute
+ pass
+
+ add_entry(
+ {
+ "url": url,
+ "title": soup.title.text,
+ "date": utc_date(),
+ "html": main.decode_contents(), # inner HTML of main container
+ },
+ dryrun=dryrun,
+ )
+
+
+if __name__ == "__main__":
+ args = sys.argv
+
+ if len(args) == 1:
+ print(f"Usage: {args[0]} [<blogpath>|<comicnum>] [--dry]")
+ exit(1)
+
+ dryrun = len(args) >= 3 and args[2] == "--dry"
+
+ if args[1].isdecimal():
+ add_comic(args[1], dryrun)
+ else:
+ add_blogpost(args[1], dryrun)
diff --git a/atom/scp b/atom/scp
new file mode 100755
index 0000000..8e1d84f
--- /dev/null
+++ b/atom/scp
@@ -0,0 +1,2 @@
+#!/usr/bin/bash
+scp atom.xml www@fkfd.me:www/feed/
diff --git a/docs/about.md b/docs/about.md
index bae178f..9d0b85c 100644
--- a/docs/about.md
+++ b/docs/about.md
@@ -4,32 +4,57 @@ Hi.
[insert generic greeting here]
-This is Frederick Yin, born 2003-03-06, he/they, connected to the internet
-for memes, computer stuff, electronics, and catpics. I have 3k+ catpics on
-my disk. Go figure.
+This is Frederick Yin, born 2003-03-06, he/him or they/them, connected to
+the internet for memes, computer stuff, electronics, and catpics.
-I live in Shanghai, UTC+8:00. To other humans, I speak Mandarin and
-English. To silicon, I speak Python and C.
+I grew up in Shanghai and now study in Ann Arbor, Michigan. To other
+humans, I speak Mandarin, English, and entry-level German. To silicon,
+I speak C, C++, Python, JavaScript, a little Rust and a tiny amount of
+Haskell.
-I am an undergrad in electrical and computer <del>engineering</del>
-exorcism, but the status quo is I can't do shit. And no, I don't know Rust
-*or* Haskell. I don't own an oscilloscope, but this is expected to change.
-I do have a ThinkPad that runs Arch Linux though. Yes, it's full of
+I am an undergrad in computer <del>engineering</del> exorcism, but the
+status quo is I can't do shit. I don't own an oscilloscope (yet). I do
+have two ThinkPads that run Arch Linux though. Yes, they're full of
stickers, why do you ask.
-Out of all things unrelated to sex, what I enjoy most are (in no
-particular order):
+Favorite things:
+
+- playing bass
+- making music
+- cats (don't own any yet but will eventually)
+- plushies (yes I have two blåhaj)
+- PCBs
+- linux
+
+Interested in:
+
+- space-themed science fiction
+- languages (both natural and programming)
+- ham radio
+- accessibility
+
+Least favorite things:
+
+- capitalism
+- "web 3"
+- microsoft windows
+- differential equations
+
+Bands I listen to (in order of discovery):
-- science fiction
-- thinking about how cool space really is
- twenty øne piløts
-- collecting catpics and laughing uncontrollably to the best ones
-- plushies (yes this includes blåhaj)
-- playing air drums
-- downloading papers I do not understand
-- talking about how Alexandra Elbakyan is a hero and capitalism must be
- burned to the ground
-- impulse starting a new side project
+- I DONT KNOW HOW BUT THEY FOUND ME
+- My Chemical Romance
+- Green Day
+- Linkin Park
+- Radiohead
+
+Indie artists I follow on Bandcamp:
+
+- [Sam Aleums](https://samaleums.bandcamp.com/)
+- [Mira](https://miraonthewall.bandcamp.com/)
+- [Joe Zempel](https://joezempel.bandcamp.com/)
+- [Billy Cobb](https://billycobb.bandcamp.com/)
That's about it. Stay hydrated, don't overclock yourself, and take care.
@@ -53,7 +78,8 @@ unless otherwise stated.
### Contact
-- [Mastodon](https://mastodon.technology/@fakefred)
+- [Mastodon](https://eattherich.club/@fkfd)
+- My email in the footer
### Nobody builds their blog with MkDocs. What's wrong with you
@@ -76,12 +102,13 @@ Here is a list of tools that I ruled out:
- Hexo, Jekyll, etc: I didn't want a Node.js/Ruby environment, and I mean
it this time. Keep them away.
-## Debloated
+### Pledge of ethics
-You will not find any of these in my blogposts, as long as I am in control:
+On this blog, there will be:
-- Advertisements
-- Analytics
-- JavaScript
+- no advertisements
+- no analytics or trackers
+- no required JavaScript, if any
+- no ChatGPT-written articles
-Last updated: 2022-05-03
+Last updated: 2024-02-09
diff --git a/docs/ham/img/wartime/7300.jpg b/docs/ham/img/wartime/7300.jpg
new file mode 100644
index 0000000..b65238c
--- /dev/null
+++ b/docs/ham/img/wartime/7300.jpg
Binary files differ
diff --git a/docs/ham/img/wartime/ft5dr.jpg b/docs/ham/img/wartime/ft5dr.jpg
new file mode 100644
index 0000000..bebf24a
--- /dev/null
+++ b/docs/ham/img/wartime/ft5dr.jpg
Binary files differ
diff --git a/docs/ham/img/wartime/ts890s.jpg b/docs/ham/img/wartime/ts890s.jpg
new file mode 100644
index 0000000..362f151
--- /dev/null
+++ b/docs/ham/img/wartime/ts890s.jpg
Binary files differ
diff --git a/docs/ham/img/wartime/uv5r.jpg b/docs/ham/img/wartime/uv5r.jpg
new file mode 100644
index 0000000..216a0d4
--- /dev/null
+++ b/docs/ham/img/wartime/uv5r.jpg
Binary files differ
diff --git a/docs/ham/index.md b/docs/ham/index.md
new file mode 100644
index 0000000..83340b9
--- /dev/null
+++ b/docs/ham/index.md
@@ -0,0 +1,17 @@
+# Ham Radio
+
+I legally obtained a ham radio license on 2022-11-29.
+
+Here is a list of articles under this category.
+
+- [Joke CQ that somehow turned into a QSO](joke_cq.md)
+- [How Your Ham Radio Can Help You in Wartime](wartime.md)
+
+## Glossary
+
+- CQ: addressing whoever's on the frequency
+- QSO: amateur radio contact
+- PTT: push-to-talk
+- QTH: physical location
+- RIG: devices
+- 73: best wishes
diff --git a/docs/ham/joke_cq.md b/docs/ham/joke_cq.md
new file mode 100644
index 0000000..1b6b001
--- /dev/null
+++ b/docs/ham/joke_cq.md
@@ -0,0 +1,47 @@
+# Joke CQ that somehow turned into a QSO
+
+2022-12-09
+
+It was 01:08 local time, an hour after midnight. I was explaining what
+I could do with a ham radio license to my roommates, and powered on my
+station (UV-5R). My roommate wanted to know if the cranes in the
+construction site were still in operation, so I scanned from 430.000 MHz.
+It soon became evident not a single fucking soul was on air.
+
+He joked, "hey what if *you* called? Will anyone respond? That's legal,
+right? Given you tell them your callsign and, uhh, stuff."
+
+"Yeah of course," I said. "Wanna try?"
+
+"Haha why not," he turns to the other roommate who's gaming, "hey here's
+your chance to preach about
+[Jiaran](https://zh.wikipedia.org/wiki/%E5%98%89%E7%84%B6)" and he was
+like "lmao alright whatever"
+
+I began transmission, "CQ CQ CQ This is BH4FXW Bravo Hotel Four Foxtrot
+Xray Whiskey here's a message from my roommate" *hands roommate the mic*
+
+"…?? please subscribe to Jiaran thanks meow"
+
+I released the PTT button. "See? Your voice has been broadcast to everyone
+within about one kilometer."
+
+And that was the end of the story. Except it's not. The backlight went on
+and a voice goes:
+
+"BH4FXW this is BH4(redacted) Bravo Hotel Four (redacted). Why are you
+still awake?"
+
+I recognized this callsign, it's my ham friend upstairs.
+
+"OK listen this began as a joke between me and my roommate, but how the
+hell are you on this frequency too"
+
+"This is a pretty common frequency to moniter dude"
+
+Then we exchanged our QTH, RIG and all sorts of parameters and 73'd this
+conversation. I'm glad I didn't go too far in my CQ call I thought no one
+would hear.
+
+The room explodes in laughter for obvious reasons, and that's basically
+how my second QSO went.
diff --git a/docs/ham/wartime.md b/docs/ham/wartime.md
new file mode 100644
index 0000000..36ac576
--- /dev/null
+++ b/docs/ham/wartime.md
@@ -0,0 +1,103 @@
+# How Your Ham Radio Can Help You in Wartime
+
+2023-04-27
+
+_Note: This post is satirical. Please do not actually do this in a crisis.
+It is advised that you never do this in peacetime, either._
+
+The radio amateur is patriotic. As ham radio operators, we are prepared to
+fight for our country in case of invasion. What makes us different from
+ordinary people is our skills and equipment. Here is how they can help you
+in wartime.
+
+## ICOM 7300
+
+![Rectangular box with fancy front panel](img/wartime/7300.jpg)
+
+Few can match ICOM in build quality. This Japanese radio manufacturer is
+known to make products that last. Mere shockwaves and bulletshells simply
+cannot kill it. Solid as a rock, the ICOM 7300 is your perfect choice
+whenever you need some height, weight, or both. Can't jump because you
+lost a leg? No problem! Just step on your ICOM 7300, and get that aspirin
+in the medicine cabinet. Afraid that the nuclear shockwave might dismantle
+your tent? Just put four of them in the corners, and sleep like a baby
+— nothing's gonna blow away 15 kilograms today!
+
+## Yaesu FT5DR
+
+![Radio with a long antenna](img/wartime/ft5dr.jpg)
+
+Fabulous as it is, your trusty ICOM 7300 is not always there when you need
+it. Sometimes all you have is a portable radio, like the renowned Yaesu
+FT5DR.
+
+So how can the FT5DR help you at the face of an invader? It's simple, but
+requires a fair amount of agility and precision. If you ever find yourself
+cornered, install the antenna, grip the FT5DR firmly, and stab them. If
+you hit them at the right spot, they will bleed, and drop their weapon.
+This is your chance to escape.
+
+__Bonus point!__ The FT5DR is IPX7 waterproof, which ensures the blood of
+your enemy does not damage the inner circuit board.
+
+## Baofeng UV-5R
+
+![Radio with a black plastic chassis](img/wartime/uv5r.jpg)
+
+The UV-5R is a budget mobile transceiver, rated 5W and covering VHF and
+UHF bands. Priced at $20, it is truly one of the most affordable beginner
+models, and there exists plenty of documentation, including schematics.
+This makes it an ideal choice for crafting IEDs. Once you have your
+detonator set up, leave it on the doorsteps of your enemy and wait for the
+dynamite to blow up.
+
+Also, if you don't plan to make IEDs, the UV-5R makes a great flashlight.
+
+## Kenwood TS-890S
+
+![Wide radio box with a color LCD and lotta knobs](img/wartime/ts890s.jpg)
+
+We saved the best for the last. What makes Kenwood stand out among its
+competitors is its experience in not only ham radio, but also professional
+hi-fi audio and entertainment systems. It is a venerable company in all
+these markets.
+
+Kenwood makes hundreds of impressive products, and this model is no
+exception. It transmits over HF at 100W, operates on CW, RTTY and PSK, and
+has a fancy live spectrum monitor screen. Under ideal conditions, your
+signal can reach any corner of the world.
+
+When you find yourself direly injured and none of the therapies seem to
+work, your Kenwood TS-890S can help you. First, wipe the blood off your
+hand because you don't want a short circuit in any of the connectors.
+Then, go somewhere to die alone. You don't want your radio waves to draw
+hostile forces to your squadmates.
+
+Using the last minutes of your life, key out your callsign one last time.
+Your fingers might not be as swift, but your Kenwood compensates for it
+with near-perfect fidelity, even across continents. Broadcast your final
+message to the world over the air. Your friends will hear you. Your
+enemies will hear you. The intel agencies will hear you. People sitting in
+office chairs will not hear you, but if you try hard enough someone will
+complain to them about you. The message may consist of only a few words,
+such as "GM HR IS [your name] GOODBYE WORLD 73".
+
+## Purpose of this overcomplicated shitpost
+
+I once saw a similarly titled article written by a ham who took themselves
+too seriously. In a war, either enlist and use the army stuff (which is
+better quality than most amateur rig anyway), or be a civilian and shut
+the fuck up.
+
+You should never expose your location to invaders. Unfortunately, the
+antenna of a radio does exactly that. The only scenario where I would push
+the PTT button is if I fell into a hole, and radio is the only way out.
+Otherwise, there is absolutely no reason to help your enemy triangulate
+your air raid shelter.
+
+## Image credits
+
+- [Baofeng UV-5R](https://www.baofengradio.com/products/uv-5r)
+- [Yaesu FT5DR](https://www.gpscentral.ca/product/yaesu-ft5dr/)
+- [ICOM 7300](https://www.amazon.com/dp/B01C95F56M)
+- [Kenwood TS-890S](https://www.amazon.com/dp/B07K1MHR48)
diff --git a/docs/img/icons/about.png b/docs/img/icons/about.png
new file mode 100644
index 0000000..4043190
--- /dev/null
+++ b/docs/img/icons/about.png
Binary files differ
diff --git a/docs/img/icons/comics-hover.png b/docs/img/icons/comics-hover.png
new file mode 100644
index 0000000..dab498e
--- /dev/null
+++ b/docs/img/icons/comics-hover.png
Binary files differ
diff --git a/docs/img/icons/comics.png b/docs/img/icons/comics.png
new file mode 100644
index 0000000..03a9904
--- /dev/null
+++ b/docs/img/icons/comics.png
Binary files differ
diff --git a/docs/img/icons/fediring.kra b/docs/img/icons/fediring.kra
new file mode 100644
index 0000000..2917542
--- /dev/null
+++ b/docs/img/icons/fediring.kra
Binary files differ
diff --git a/docs/img/icons/fediring.png b/docs/img/icons/fediring.png
new file mode 100644
index 0000000..a56d426
--- /dev/null
+++ b/docs/img/icons/fediring.png
Binary files differ
diff --git a/docs/img/icons/fediring_next.png b/docs/img/icons/fediring_next.png
new file mode 100644
index 0000000..03575b5
--- /dev/null
+++ b/docs/img/icons/fediring_next.png
Binary files differ
diff --git a/docs/img/icons/fediring_prev.png b/docs/img/icons/fediring_prev.png
new file mode 100644
index 0000000..017f430
--- /dev/null
+++ b/docs/img/icons/fediring_prev.png
Binary files differ
diff --git a/docs/img/icons/feed.png b/docs/img/icons/feed.png
new file mode 100644
index 0000000..b0994be
--- /dev/null
+++ b/docs/img/icons/feed.png
Binary files differ
diff --git a/docs/img/icons/ham-hover.png b/docs/img/icons/ham-hover.png
new file mode 100644
index 0000000..61d5bb9
--- /dev/null
+++ b/docs/img/icons/ham-hover.png
Binary files differ
diff --git a/docs/img/icons/ham.png b/docs/img/icons/ham.png
new file mode 100644
index 0000000..71d4f87
--- /dev/null
+++ b/docs/img/icons/ham.png
Binary files differ
diff --git a/docs/img/icons/icons.kra b/docs/img/icons/icons.kra
new file mode 100644
index 0000000..4fff115
--- /dev/null
+++ b/docs/img/icons/icons.kra
Binary files differ
diff --git a/docs/img/icons/links.png b/docs/img/icons/links.png
new file mode 100644
index 0000000..5d78c41
--- /dev/null
+++ b/docs/img/icons/links.png
Binary files differ
diff --git a/docs/img/icons/music-hover.png b/docs/img/icons/music-hover.png
new file mode 100644
index 0000000..c2e3b49
--- /dev/null
+++ b/docs/img/icons/music-hover.png
Binary files differ
diff --git a/docs/img/icons/music.png b/docs/img/icons/music.png
new file mode 100644
index 0000000..61fd0eb
--- /dev/null
+++ b/docs/img/icons/music.png
Binary files differ
diff --git a/docs/img/icons/projects-hover.png b/docs/img/icons/projects-hover.png
new file mode 100644
index 0000000..5a8480e
--- /dev/null
+++ b/docs/img/icons/projects-hover.png
Binary files differ
diff --git a/docs/img/icons/projects.png b/docs/img/icons/projects.png
new file mode 100644
index 0000000..1833b84
--- /dev/null
+++ b/docs/img/icons/projects.png
Binary files differ
diff --git a/docs/img/icons/random-hover.png b/docs/img/icons/random-hover.png
new file mode 100644
index 0000000..19d1d08
--- /dev/null
+++ b/docs/img/icons/random-hover.png
Binary files differ
diff --git a/docs/img/icons/random.png b/docs/img/icons/random.png
new file mode 100644
index 0000000..7546fb6
--- /dev/null
+++ b/docs/img/icons/random.png
Binary files differ
diff --git a/docs/img/icons/shitpost-hover.png b/docs/img/icons/shitpost-hover.png
new file mode 100644
index 0000000..df11755
--- /dev/null
+++ b/docs/img/icons/shitpost-hover.png
Binary files differ
diff --git a/docs/img/icons/shitpost.png b/docs/img/icons/shitpost.png
new file mode 100644
index 0000000..2145e99
--- /dev/null
+++ b/docs/img/icons/shitpost.png
Binary files differ
diff --git a/docs/img/icons/ta-hover.png b/docs/img/icons/ta-hover.png
new file mode 100644
index 0000000..f1dfe7e
--- /dev/null
+++ b/docs/img/icons/ta-hover.png
Binary files differ
diff --git a/docs/img/icons/ta.png b/docs/img/icons/ta.png
new file mode 100644
index 0000000..7c13da4
--- /dev/null
+++ b/docs/img/icons/ta.png
Binary files differ
diff --git a/docs/img/icons/umich-hover.png b/docs/img/icons/umich-hover.png
new file mode 100644
index 0000000..da636ea
--- /dev/null
+++ b/docs/img/icons/umich-hover.png
Binary files differ
diff --git a/docs/img/icons/umich.png b/docs/img/icons/umich.png
new file mode 100644
index 0000000..98799c4
--- /dev/null
+++ b/docs/img/icons/umich.png
Binary files differ
diff --git a/docs/img/icons/x.png b/docs/img/icons/x.png
new file mode 100644
index 0000000..0018a03
--- /dev/null
+++ b/docs/img/icons/x.png
Binary files differ
diff --git a/docs/index.md b/docs/index.md
index ff54025..18515c3 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,40 +1,90 @@
-# fkfd
+---
+title: Home
+---
-This is technically a blog.
-
-- [About Me](/about)
-
-- [Atom feed](/feed/atom.xml)
+<!-- written in HTML to disable the ¶ thing on page title -->
+<h1>fkfd.me — <a href="/about">about</a></h1>
## Categories
-- [Projects](/projects)
-
-- [Random](/random)
-
-- [Shitpost](/shitpost)
-
-## Non-blog Content
-
-- [Comics](/comics)
-
-## Fun
-
-- [ascii bio](/x)
+<div class="category-block-container">
+ <a href="projects/">
+ <div class="category-block">
+ <span class="category-icon" id="icon-projects"></span>
+ <span class="category-text">Projects</span>
+ </div>
+ </a>
+ <a href="random/">
+ <div class="category-block">
+ <span class="category-icon" id="icon-random"></span>
+ <span class="category-text">Random</span>
+ </div>
+ </a>
+ <a href="music/">
+ <div class="category-block">
+ <span class="category-icon" id="icon-music"></span>
+ <span class="category-text">Music</span>
+ </div>
+ </a>
+ <a href="umich/">
+ <div class="category-block">
+ <span class="category-icon" id="icon-umich"></span>
+ <span class="category-text">UMich</span>
+ </div>
+ </a>
+ <a href="shitpost/">
+ <div class="category-block">
+ <span class="category-icon" id="icon-shitpost"></span>
+ <span class="category-text">Shitpost</span>
+ </div>
+ </a>
+ <a href="comics/">
+ <div class="category-block">
+ <span class="category-icon" id="icon-comics"></span>
+ <span class="category-text">Comics</span>
+ </div>
+ </a>
+ <a href="ta/">
+ <div class="category-block">
+ <span class="category-icon" id="icon-ta"></span>
+ <span class="category-text">TA</span>
+ </div>
+ </a>
+ <a href="ham/">
+ <div class="category-block">
+ <span class="category-icon" id="icon-ham"></span>
+ <span class="category-text">Ham Radio</span>
+ </div>
+ </a>
+</div>
+
+## Favorite blogposts
+
+- The nand2tetris series where I build a computer
+
+ - [part 1](projects/nand2tetris_1.md): logic circuits
+ - [part 2.1](projects/nand2tetris_2.1.md): virtual stack machine
+ - [part 2.2](projects/nand2tetris_2.2.md): tokenizer
+ - part 2.3: WIP indefinitely
+
+- Scraping lyrics, counting words and making a game out of twenty øne
+ piløts songs: [One tøp song](projects/one_top_song.md)
+
+- A ROM upgrade that went shitfire prompted me to write [The most
+ important part of a smartphone is the OS.](random/smartphone_os.md)
+
+- Continuously updating: [My life goals](random/life_goals.md)
+
+## For fun
- [bongo cat](/toys/bongo) — [bongo.cat](https://bongo.cat), de-Googled,
de-Cloudflared
- [emojo](/toys/emojo) — copy of
- [emojo.site](https://github.com/bclindner/emojo.site)
+ [emojo.site](https://github.com/bclindner/emojo.site) (warning: plays music)
- [one tøp song](/toys/one_top_song) — a game of words that only appear in
one twenty øne piløts song
-## Fediring
-
-Websites of fellow members on Fediring.
-
-[👈 Previous](https://fediring.net/previous?host=fkfd.me)
-[🪐 Fediring](https://fediring.net/)
-[👉 Next](https://fediring.net/next?host=fkfd.me)
+- [< lt / gt >](/toys/ltgt) — statistics of my personal life (but you can
+ only see <, =, or >)
diff --git a/docs/links.md b/docs/links.md
new file mode 100644
index 0000000..8304e79
--- /dev/null
+++ b/docs/links.md
@@ -0,0 +1,66 @@
+# Links
+
+Corners of the internet I enjoy.
+
+## Blogs
+
+### [mitxela](https://mitxela.com/)
+
+An inventor and artist. Known for tiny projects like [World's Smallest
+MIDI Synthesizer](https://mitxela.com/projects/smallest_midi_synth) (later
+topped by [An even smaller, even sillier
+synth](https://mitxela.com/projects/silly_synth)) and [UV Protection
+Amulet](https://mitxela.com/projects/amulet).
+
+### [Seirdy](https://seirdy.one/)
+
+### [paritybit.ca](https://www.paritybit.ca/)
+
+### [Dvel](https://dvel.me/)
+
+I was fascinated by their detailed account of [oddities in Chinese
+language standards](https://dvel.me/posts/chinese-character-confusion/).
+
+### [maia](https://maia.crimew.gay/)
+
+Probably the first hacker kitten to have a [Wikipedia
+article](https://en.wikipedia.org/wiki/Maia_arson_crimew). Known for
+leaking TSA's No Fly List.
+
+### [Linsy](https://blog.yydbxx.cn/)
+
+My friend (certified big math nerd and compsci geek) 's blog. He does Rust
+and OCaml stuff.
+
+## Impressive and/or fun
+
+### [v86](https://copy.sh/v86/)
+
+WASM x86 virtualization. I boot Kolibri to play its games.
+
+### [My University Is Better Than Your University](https://www.zizhengfang.com/applets/transitivity)
+
+Abuses transitivity to show that my university is, in fact, better than
+yours.
+
+### [Circuit JS](https://falstad.com/circuit/circuitjs.html)
+
+Simulates analog and digital circuits. Saved me hours of breadboarding.
+
+### [Xenia, the Linux mascot](https://xenia-linux-site.glitch.me/)
+
+Are you a transgender furry Linux hacker? I have good news for you…
+
+## Games
+
+### [3D Pinball for Windows](https://alula.github.io/SpaceCadetPinball/)
+
+Reverse engineered port of the game.
+
+### [Let's Surf](https://surf.jackbuehner.com/)
+
+Port of the game otherwise known as `edge://surf`.
+
+## Comics
+
+### [Extra Ordinary](https://www.exocomics.com/)
diff --git a/docs/music/band_discovery.md b/docs/music/band_discovery.md
new file mode 100644
index 0000000..380257a
--- /dev/null
+++ b/docs/music/band_discovery.md
@@ -0,0 +1,328 @@
+# How I Discover a Band
+
+2024-03-16
+
+I listen to 6 bands. By "listening to" a band, I mean I have at least two
+albums and regularly put them on, at least once a month.
+
+An outline of the process of me discovering a band:
+
+- Listen to one of their songs (usually hit single)
+- Ooh I like it
+- Listen to other songs
+- Hmm some of these are good
+- Listen again
+- Woah most of these are good
+- OK I guess I like the band now
+
+The process can be modeled as a function of E (how much I enjoy <insert
+band name\>) over t (time). E(t) is composed of three stages:
+
+- Linear growth (t0 to t1, 0 to E1)
+- Constant (t1 to t2, E1)
+- Logistic growth (t2 to infinity, E follows an S-curve: it first takes
+ off exponentially but flattens and converges to an asymptote at EM)
+
+![Diagram of said function](img/band_discovery/curve.png)
+
+If EM is greater than a threshold, I decide that I like the band. As
+I discover and/or they release more music, E might grow beyond EM.
+
+In general,
+
+- t0: hit single
+- t1: hit single on repeat
+- t2: I become interested in other songs
+
+Now, we just need to find these parameters for every band.
+
+## twenty one pilots
+
+### t0: July 2016, "Stressed Out"
+
+That was the time when twenty one pilots were as hot as the sun. "Stressed
+Out" and "Heathens" were everywhere. I was on a flight with no headrest
+screen, and the only form of entertainment was a tiny overhead TV (boring)
+and a headphone jack on the armrest. It let you pick a channel, and
+channel 13 was playing Billboard songs which happened to include "Stressed
+Out". I thought it sounded special, like "I'm 14 and this is deep" kind of
+special (I was, in fact, 13, so this is incredibly on brand). Anyway, as
+soon as I landed I streamed it on repeat.
+
+### t1: August 2016, _Blurryface_
+
+Didn't take long to find out that other songs on _Blurryface_ are even
+better. My favorites were "Heavydirtysoul", "Ride", and "Lane Boy".
+"Polarize", "Hometown" and "Goner" are good as well.
+
+I listened to _Blurryface_ every week, but not any other album. Once I put
+on "Implicit Demand For Proof", but the intro was too long for my liking.
+
+### t2: August 2017, _Vessel_
+
+One summer afternoon I was doing dishes. After a year of listening to one
+album, I grew bored. So I put on their 2013 album, _Vessel_.
+
+Completely honest reaction: my jaws dropped at how good "Ode to Sleep"
+was. Then how good "Holding on to You" was. All the way to "Car Radio",
+which to this day remains my favorite song.
+
+"Ode to Sleep" was even special-er. 5:08 long and full of tempo changes,
+it was the closest thing to Bohemian Rhapsody tøp have made. (I at that
+time had never heard of Bohemian Rhapsody, but I would have enjoyed it.)
+
+### Soon after t2
+
+_Vessel_ got me into the rabbit hole. Very soon I put on _Regional at
+Best_, and "Kitchen Sink" became one of my favorites. (Funny because
+I literally discovered _Vessel_ at a kitchen sink.) Then _Twenty One
+Pilots_ (aka Self-Titled), which I obsessively listened to in June 2018,
+even on a trans-Pacific flight.
+
+Unfortunately that was everything they had made. I downloaded their live
+performances, covers, and Tyler Joseph's solo album, _No Phun Intended_.
+They have shaped my teenage years.
+
+### 2018, _Trench_
+
+In July 2018 they left hiatus and announced _Trench_, which is my first
+experience of anticipating an album and discussing the lore. It dropped on
+2018-10-05, a date I will never forget. It remains my favorite record.
+
+Fav tracks (a tough choice): "Morph", "Chlorine", "Nico And The Niners",
+"Bandito"
+
+Trivia: my previous laptop's hostname is bandito; my current one's is
+chlorine.
+
+### 2021, _Scaled And Icy_
+
+One night as I'm about to go to bed, I checked Wikipedia for no reason and
+apparently a few days ago tøp had announced a new album. It was totally
+a coincidence.
+
+Later one Thursday afternoon I took sick leave from school, and when I got
+home I found the music video for "Shy Away" just dropped. Totally another
+coincidence…
+
+_Scaled And Icy_ (2021) was not as soul-wrenching as _Trench_, because as
+Tyler explained, the depressed world does not need yet another depressing
+album. They had an online concert which I watched for like $20, which is
+heck of a performance. There was 10 times more props, visuals and
+choreography than a regular tour. It was flawless (other than Josh
+dropping his drumstick at the end of "Car Radio", but he quickly grabbed
+another one without missing, quite literally, a beat).
+
+### 2024, _Clancy_
+
+And now in 2024 there's another record in the oven. How did I find out?
+Wikipedia! Just need to wait…
+
+"Overcompensate" is an absolute bop.
+
+## I DONT KNOW HOW BUT THEY FOUND ME
+
+### t0: some time in 2018, "Choke"
+
+I discovered the music video for "Choke" and thought wow that sounded
+cool.
+
+### t1: some time in 2019, _1981 EP_
+
+Later I downloaded _1981 EP_. It was the first time I bought music with
+money. I remember the summer when I played this on the bus.
+
+Now, because they were a new band, there wasn't much backlog discog to
+explore.
+
+### t2: October 2020, _Razzmatazz_
+
+It's a really good record and I liked it instantly. In March 2021 I bought
+it as a birthday treat to myself.
+
+Fav tracks: "Leave Me Alone", "Clusterhug", "Razzmatazz"
+
+### December 2023
+
+IDKHOW announced a tour for their upcoming album _GLOOM DIVISION_, and
+I got a ticket. The only downside (pun intended; "DOWNSIDE" is a track
+title) is it's in Detroit. My advisor had once told me not to go to
+Detroit alone. I'm gonna do it anyway hope I don't die lmao
+
+So the album is out now in February 2024. Fav tracks: "DOWNSIDE",
+"INFATUATION", "SIXFT"
+
+## My Chemical Romance
+
+### t0: 2020-ish, "This is How I Disappear"
+
+tøp covered "Cancer", but it doesn't count. My real first exposure to MCR
+is either "Welcome to the Black Parade" or "This is How I Disappear" (I
+probably found this video: [This Is How I Disappear but it’s just Gerard’s
+vocals in a large
+cathedral](https://www.youtube.com/watch?v=lITNYUmM50g)).
+
+Later I added "Cancer", "Mama", and "Sleep" into the list. These songs got
+me obsessed — for a little while. I almost never listened to them again.
+Until 2021.
+
+### t1: 2021, _The Black Parade_
+
+The summer I graduated high school, I went to a driving school. On my way
+home I put on _The Black Parade_ in its entirety and it was better than
+I remembered. But still, no progress.
+
+### t2: 2022, _Three Cheers_
+
+Forgot why, but I randomly decided to put on _Three Cheers_. I had already
+listened to "Helena" and "The Ghost Of You", but not the rest of the
+album.
+
+It was fire. Even better than _Parade_. Fav: "Helena", "Give 'Em Hell,
+Kid", "It's Not A Fashion Statement, It's A Fucking Deathwish", "I Never
+Told You What I Do For A Living".
+
+### August 2022, _Danger Days_
+
+I can hardly believe this is the same band that made _Three Cheers_, but
+it's fire nevertheless.
+
+The semester began in September with unpleasant memories, and they got
+kinda associated with some of the songs (especially "The Only Hope for Me
+is You"), but it is what it is.
+
+Fav: "Na Na Na (Na Na Na Na Na Na Na Na Na)", "Bulletproof Heart",
+"S/C/A/R/E/C/R/O/W", "Summertime".
+
+### November-ish 2022, _Bullets_
+
+MCR's debut banger. I listened to it in the final exam weeks. After I came
+back home, I got covid, but once I awoke I would blast _Bullets_. It was
+the record I jammed to when I was learning Haskell.
+
+Fav: "Drowning Lessons", "Early Sunsets Over Monroeville", "Demolition
+Lovers".
+
+### 2023, _Conventional Weapons_
+
+Fav: "Boy Division", "Gun.", "The World Is Ugly".
+
+And thus I have finished MCR's discography. I wrote a review: [Review of
+My Chemical Romance discography](mcr_discog_review.md).
+
+## Green Day
+
+### t0 and t1: December 2022, "21 Guns"
+
+I was preparing for SoulRocker (campus rock show, lots of bands there) and
+saw "21 Guns" on the setlist. This seems to be the only song I had
+a chance of jamming to, because the rest were either older than my parents
+or weeb music. So I checked out Green Day and learned the lyrics to "21
+Guns".
+
+The performance was a bop, everyone was high af, but nobody's throwing up
+their arms into the sky.
+
+### t2: March 2023, _American Idiot_
+
+This is the first time where the hit single did _not_ lure me into its
+album. Instead I listened to this popular choice.
+
+My favorite was "Jesus of Suburbia", which is the Bohemian Rhapsody of
+punk rock. Other favs are "Letterbomb", "Wake Me Up When September Ends",
+and "Whatsername".
+
+### March 2023, _21st Century Breakdown_
+
+This time I got the album "21 Guns" is from. Honestly, it's not as
+consistently good as _Idiot_. I would skip tracks.
+
+Fav: "21st Century Breakdown", "Before the Lobotomy".
+
+I would skip: "Christian's Inferno", "East Jesus Nowhere", and on a bad
+day everything after "Restless Heart Syndrome" except "21 Guns".
+
+### April 2023, _Dookie_ and _Nimrod_
+
+They have not grown on me (yet). If I feel like it, I might listen to them
+again.
+
+## Linkin Park
+
+### t0 and t1: March 2023, "In The End"
+
+u/SorridoSnake (deleted account) on Reddit posted this on r/sbubby:
+[Posting a Sbubby for every lyric of Linkin Park’s “In The End” until
+I complete the whole
+song](https://www.reddit.com/r/sbubby/comments/1184kep/posting_a_sbubby_for_every_lyric_of_linkin_parks/)
+
+and I thought hey why not listen to this song
+
+(I know I am terminally late, I know it's a 2001 song, but this is the
+first time I listened to it seriously, like not from a radio or
+loudspeakers in some supermarket)
+
+Once the song finishes I know I have a new band to check out. (Don't ask
+why it took a month)
+
+### t2: April 2023, _Hybrid Theory_
+
+This is the first band I listened to with two lead vocalists. I like
+Mike's rap lines as much as Chester's screaming. They formed my basic
+understanding of rap rock, and later inspired me to make [This Song Will
+Uncure Your Depression](uncure-your-depression.md).
+
+Fav: "Papercut", "In The End", "My December".
+
+### June 2023, _Meteora_
+
+It's as good as _Hybrid_, but I got distracted and never had a "hey what
+if" moment to listen to more Linkin Park.
+
+## Radiohead
+
+In May 2023, I heard a band cover "Creep" but didn't investigate further,
+mistaking Radiohead with Just Another Band Talking About Girls And Stuff.
+Big nope in hindsight.
+
+### t0: January 2024, "My Iron Lung"
+
+I watched this video titled ["My Iron Lung Looks Just Like Buddy
+Holly"](https://www.youtube.com/watch?v=2KGRvIFrF3c). It is incredibly
+stupidly well-made. I will show the transcript:
+
+> 0:00 [Depressed Radiohead sounds] BaawwwhDooDeeDoo
+> 0:01 [Depressed Radiohead sounds] WawwhDooDeeDoo
+> 0:02 [Depressed Radiohead sounds] BowDooDiiOhDaaiiiiiaaaaDo
+> 0:05 [Depressed Radiohead sounds] WohhhOoDeeOo
+> 0:06 [Depressed Radiohead sounds] HehhOoDeeOo
+> 0:07 DADOODADEEDIIIIIEEDOODOO
+> 0:10 OOHWEEOOH AILUKJUHSLAIBUDIHAAALIIIIII
+> 0:15 OOHWEEOOH AILUKJUHSLAIBUDIHAAALIIIIII BOW
+> 0:15 OH OH ANYURMERITAILERMOOOOOR
+
+Naturally, the words "Depressed Radiohead sounds" caught my attention and
+naturally I wanted to hear what the whole song sounded like. It absolutely
+rocks.
+
+This is the first time I discovered a band from a silly video.
+
+### t1: January 2024, "The Bends"
+
+Immediately I listened to _The Bends_. Instant favorites are "Planet
+Telex", "(Nice Dream)", and "Street Spirit". Very depressed indeed.
+
+### t2: February 2024, _OK Computer_ and _Kid A_
+
+I tried OK Computer but did not find it as good as either _The Bends_ or
+_Kid A_. "Paranoid Android" is a banger but the rest sounded awkward, like
+early CGI. Especially the synth in "Exit Music" that sounds like it came
+straight from a carpenter's workshop.
+
+_Kid A_ did the trick for me. It's just undisguised EDM and sounds much
+more polished. Favorites: "Everything in its Right Place", "The National
+Anthem", "Idioteque", "Morning Bell". Love the 10/8 and 5/8 signature.
+
+Later in March I revisited _OK Computer_ and admitted I was wrong. The
+tracks are pretty good actually. Favorites: "Airbag", "Paranoid Android",
+"Let Down", "No Surprises".
diff --git a/docs/music/clancy_review.md b/docs/music/clancy_review.md
new file mode 100644
index 0000000..d4f95d4
--- /dev/null
+++ b/docs/music/clancy_review.md
@@ -0,0 +1,111 @@
+# Review of _Clancy_ (2024)
+
+Last Updated 2024-03-31
+
+## Overcompensate
+
+The first single dropped while I was in Boston. I watched the MV in the
+lobby at the Museum of Fine Art. I cannot say a bad thing about the music.
+
+Things that immediately caught my attention:
+
+- So the boys decided to start an entire album with German (I heard "diese
+ kleine", "gemacht" and "wir glauben beide", but I couldn't figure out
+ the rest until someone on Reddit transcribed it)
+- Interpolation of "Bandito" from _Trench_
+- First "real" line of song begins at 1:46 which, depending on your
+ definition, may or may not make the intro even longer than in "Implicit
+ Demand For Proof"
+- Tyler grins every time he sings the word "overcompensate"
+- His hand signs V on "twice" which is a classic trope all tøp fans
+ recognize
+- On "by the time", he pretends to check his watch, alluding to the short
+ published in 2022 titled "sometimes people ask what we do all day before
+ the show starts." where he does the same
+
+![Left: Tyler sits on a couch and looks at his wrist; right: Tyler stands
+in a red light and looks at his wrist](img/clancy_review/watch.jpg)
+
+- At the end, someone pulls off "Tyler"'s mask only to find another
+ person. Tyler himself is actually far away from Dema, and the "Tyler" we
+ saw throughout the video is just a citizen possessed by Tyler. The
+ citizen has long hair and looks feminine, which is coherent with a fan
+ theory (which I believe) that "Redecorate" from _Scaled And Icy_ is
+ about a transgender person.
+- Josh ordered his like 6th? 7th? 314th? custom drum kit from SJC??
+
+Only problem I have is the youtube thumbnail. Look at Josh. Why he look
+like Elon Musk 😭😭😭
+
+![Josh in front of a projection screen and a line of text is projected
+right on his lips](img/clancy_review/overcompensate_thumbnail.jpg)
+
+## The album cover & meta things
+
+The album cover was not well received. Lots of fans say they could do
+better. I agree. Just look at this.
+
+![Tyler and Josh, in black and white all blurry like crafty newpaper
+cutouts, in front of a red/yellow
+background](img/clancy_review/clancy.jpg)
+
+The art style is ok, and it's reminiscent of the low-res, dithered feel of
+their teasing website,
+[dmaorg.info](http://dmaorg.info/found/15398642_14/clancy.html). It's fine
+if it's the style they're going for. After all it's the first time the
+boys put themselves on a record cover.
+
+But I mean… at least give Josh more attention… like c'mon.
+
+The album is set to drop May 17, which made me worried because if they
+tour right then over summer break I might miss it.
+
+## Next Semester
+
+Haha! They're not touring until August, and the Detroit show is on
+September 29. I guess you could literally… (picks up acoustic guitar) wake
+me up when september ends 🥁🥁🥁
+
+Anyway, a new single dropped titled "Next Semester" and incidentally
+that's when I'll go to their show. I received the newsletter halfway
+through a take-home exam, and I was unable to concentrate ever since.
+
+The song is unexpectedly punk, a _huge_ deflection from the hip-hop of
+"Overcompensate". The bass is punchy af and sounds straight from Mike
+Dirnt. The drumline is almost irresponsibly simple compared to
+"Overcompensate". It's un-Josh.
+
+_Snap, snap, snap — it's not a bad thing._ Given the punk genre, this may
+be intentional. Allow me to go on a tangent about Radiohead.
+
+Back when I was in love with _The Bends_ and thought nothing could top it,
+I tried _Kid A_ and, unexpectedly, it got me hooked in just three seconds.
+For context, _The Bends_ is pure rock, and _Kid A_ is pure electronic
+— synthesizers, drum machines, vocoders, etc. If Wikipedia is to be
+trusted, it's because Radiohead grew tired of rock.
+
+You need dynamics in a song, you need dynamics in an album, and you need
+dynamics in your discography. To make "[genre] and [genre] only" is to
+become a machine. This is why I get bored of some albums by Linkin Park
+and Green Day. To be fair, I get bored with tracks #6-9 on _Vessel_
+sometimes, so I'm not as biased as you think.
+
+Back to "Next Semester". Interesting things:
+
+- Tyler's bass is practically identical to mine; he's got a Fender P,
+ probably charcoal gray, and I've got a Squier PJ in the same color.
+ Great minds think alike
+- Despite probably intended to resonate with students, it did not resonate
+ with me because I'm doing pretty ok this semester and am not
+ contemplating suicide no thank you will check back when i need it
+
+Now.
+
+Tickets.
+
+This is the most stressful part, to get tickets before they sell out,
+probably not into hands of real fans, but resellers who profit with an
+obscene margin. I hope they die.
+
+Here's hoping my luck works out on April 2. I hope it's less than $100,
+but at this point it's not really about cost.
diff --git a/docs/music/deathbed_playlist.md b/docs/music/deathbed_playlist.md
new file mode 100644
index 0000000..709224e
--- /dev/null
+++ b/docs/music/deathbed_playlist.md
@@ -0,0 +1,17 @@
+# Playlist to put on on my deathbed
+
+2023-03-11
+
+Memorandum provided without context and subject to revision without
+notice. But seriously these are good songs.
+
+- Wake Me Up When September Ends
+- Kitchen Sink
+- Demolition Lovers
+- Choker
+- Bohemian Rhapsody
+- Ode To Sleep
+- House Of Gold
+- Car Radio
+- Taxi Cab
+- Street Spirit (Fade Out)
diff --git a/docs/music/early-sunsets-over-monroeville.md b/docs/music/early-sunsets-over-monroeville.md
new file mode 100644
index 0000000..97b8212
--- /dev/null
+++ b/docs/music/early-sunsets-over-monroeville.md
@@ -0,0 +1,152 @@
+# Early Sunsets Over Monroeville
+
+2023-01-16
+
+One evening, Mom entered my room when I practiced ukulele. She was like
+"you should record smth" so I did.
+
+I went for Early Sunsets Over Monroeville ("Monroeville" hereafter) by My
+Chemical Romance (MCR), for following reasons:
+
+- I happened to be playing that song
+- It's one of the few MCR songs acoustic enough for the ukulele, the only
+ pitched instrument I can play
+- [Billy Cobb made a cover](https://www.youtube.com/watch?v=OVYs4kvBii8)
+ and I enjoyed it immensely
+- There is one more reason but it's complicated so I put it at the end
+
+The work began on 2023-01-12 and ended on 2023-01-15.
+
+## Step 1: Learn song
+
+Monroeville is structured as follows:
+
+- intro
+- verse 1
+- prechorus 1
+- verse 2
+- prechorus 2
+- verses 3, 4, 5 that have more or less the same lyrics but get
+ increasingly emotional
+
+I found [the ukulele
+tab](https://ukutabs.com/m/my-chemical-romance/early-sunsets-over-monroeville/)
+and learned to play the chords. Then I spent a few showers to practice the
+vocals.
+
+## Step 2: DAW
+
+I know this can't be a one-take because I only have one mic, and the uke
+would completely demolish my untrained voice. Also, this song is like
+5 minutes long.
+
+I did try recording and putting together clips in Audacity before, but
+without bars and beats it was a pain to synchronize.
+
+This time however, I installed [Ardour](https://ardour.org/) and gave it
+a whirl. A few hours and ready to go.
+
+## Step 3: Rig
+
+My rig:
+
+- second hand soprano ukulele
+- USB lavalier mic for online lectures
+- overear headphones, never used for six years
+- a laptop
+
+## Step 3: Record strumming
+
+This is like rhythm guitar in the original composition. Chords were not
+too hard, but it took a while to play on tempo. This is me strumming the
+intro:
+
+<audio controls src="../img/early_sunsets_over_monroeville/intro_strum.ogg"></audio>
+
+## Step 4: Improvise
+
+I improvised a riff to imitate the prechorus melody. This was done by
+plucking the G string. I just clipped my nails, so to make it sound
+brighter I used a guitar pick.
+
+<audio controls src="../img/early_sunsets_over_monroeville/prechorus_plucks.ogg"></audio>
+
+## Step 5: Record drums
+
+I recorded some clips of me slapping my thighs but ended up discarding
+them because (a) they sounded off-theme and inconsistent, and (b) it was
+stupid. Also this is an acoustic cover so drumless would be just fine.
+
+![Screenshot of the editor window](img/early_sunsets_over_monroeville/editor.png)
+
+▲ It was this moment that I decided thigh drums didn't go well with
+Monroeville.
+
+## Step 6: Record lead
+
+I found [lead guitar tabs on
+Songsterr](https://www.songsterr.com/a/wsa/my-chemical-romance-early-sunsets-over-monroeville-tab-s22636)
+and transposed some of it for the ukulele. This is the prechorus lead:
+
+<audio controls src="../img/early_sunsets_over_monroeville/prechorus_lead.ogg"></audio>
+
+My fingers were not good at fretting and picking strings on tempo, but in
+a choir of tracks these imperfections were easily concealed. Near the end
+I accidentally rang strings I wasn't supposed to touch, but luckily they
+were somewhat in key.
+
+## Step 7: Record vocals
+
+It took a few tries to place the mic somewhere stable enough, but there's
+still this kind of low-frequency oscillations in the waveform:
+
+![](img/early_sunsets_over_monroeville/mic_oscillations.png)
+
+Good news is it's barely noticeable.
+
+I could not go as hard and emotional as Gerard or Billy in the second half
+because (a) I never trained myself to scream, and (b) I've never had any
+experience with a zombiefied romantic partner. As a result, the second
+half sounds emotionless, which is a tradeoff between that and being
+completely out-of-tune.
+
+## Step 8: Mix
+
+It's just playing with faders, panning the tracks, and dragging some
+automation curves to conceal the noise in the vocal track when I kicked
+over a trash can.
+
+![Screenshot of the mixer window](img/early_sunsets_over_monroeville/mixer.png)
+
+▲ This was the Hackerman moment of the project.
+
+I also added a reverb filter for the vocals.
+
+## Step 9: Export!
+
+Enjoy: Early Sunsets Over Monroeville (ukulele acoustic cover)
+
+<audio controls src="/static/early_sunsets_over_monroeville.flac"></audio>
+
+Ardour gave me this window but I don't know if it's bad or good?
+
+![Peak: -2.7 dBFS, true peak: -2.7 dBTP, integrated loudness: -20.3 LUFS,
+loudness range: 6.4 LU. Below are waveform, spectrogram and
+LUFS-over-time.](img/early_sunsets_over_monroeville/export_analysis.png)
+
+## What's the one last reason I did this?
+
+twenty one pilots did a livestream recently where Tyler shared a few demos
+of theirs tracing back to the Regional at Best era, circa 2010-2012. Here
+we have a clip (MP4, 17.4 MiB):
+
+<video controls>
+ <source src="/static/vessel_stream_trees_demo.mp4" type="video/mp4">
+</video>
+
+Tyler put the gang vocal track on solo where he was like "__HELLO__ e-yeah
+a-yeah a-yeah hello hello hELLO". It was cringe af but the song was
+incomplete without it.
+
+And I, a musically illiterate engineering student, have one more reason to
+justify my own piece of carefully mixed cringe.
diff --git a/docs/music/img/band_discovery/band.kra b/docs/music/img/band_discovery/band.kra
new file mode 100644
index 0000000..2c0f762
--- /dev/null
+++ b/docs/music/img/band_discovery/band.kra
Binary files differ
diff --git a/docs/music/img/band_discovery/curve.png b/docs/music/img/band_discovery/curve.png
new file mode 100644
index 0000000..49a97b2
--- /dev/null
+++ b/docs/music/img/band_discovery/curve.png
Binary files differ
diff --git a/docs/music/img/clancy_review/clancy.jpg b/docs/music/img/clancy_review/clancy.jpg
new file mode 100644
index 0000000..7f65cbe
--- /dev/null
+++ b/docs/music/img/clancy_review/clancy.jpg
Binary files differ
diff --git a/docs/music/img/clancy_review/overcompensate_thumbnail.jpg b/docs/music/img/clancy_review/overcompensate_thumbnail.jpg
new file mode 100644
index 0000000..a02f4d3
--- /dev/null
+++ b/docs/music/img/clancy_review/overcompensate_thumbnail.jpg
Binary files differ
diff --git a/docs/music/img/clancy_review/watch.jpg b/docs/music/img/clancy_review/watch.jpg
new file mode 100644
index 0000000..abcd661
--- /dev/null
+++ b/docs/music/img/clancy_review/watch.jpg
Binary files differ
diff --git a/docs/music/img/early_sunsets_over_monroeville/editor.png b/docs/music/img/early_sunsets_over_monroeville/editor.png
new file mode 100644
index 0000000..47b49da
--- /dev/null
+++ b/docs/music/img/early_sunsets_over_monroeville/editor.png
Binary files differ
diff --git a/docs/music/img/early_sunsets_over_monroeville/export_analysis.png b/docs/music/img/early_sunsets_over_monroeville/export_analysis.png
new file mode 100644
index 0000000..cb0f944
--- /dev/null
+++ b/docs/music/img/early_sunsets_over_monroeville/export_analysis.png
Binary files differ
diff --git a/docs/music/img/early_sunsets_over_monroeville/intro_strum.ogg b/docs/music/img/early_sunsets_over_monroeville/intro_strum.ogg
new file mode 100644
index 0000000..5811be9
--- /dev/null
+++ b/docs/music/img/early_sunsets_over_monroeville/intro_strum.ogg
Binary files differ
diff --git a/docs/music/img/early_sunsets_over_monroeville/mic_oscillations.png b/docs/music/img/early_sunsets_over_monroeville/mic_oscillations.png
new file mode 100644
index 0000000..61767c9
--- /dev/null
+++ b/docs/music/img/early_sunsets_over_monroeville/mic_oscillations.png
Binary files differ
diff --git a/docs/music/img/early_sunsets_over_monroeville/mixer.png b/docs/music/img/early_sunsets_over_monroeville/mixer.png
new file mode 100644
index 0000000..a2eafd5
--- /dev/null
+++ b/docs/music/img/early_sunsets_over_monroeville/mixer.png
Binary files differ
diff --git a/docs/music/img/early_sunsets_over_monroeville/prechorus_lead.ogg b/docs/music/img/early_sunsets_over_monroeville/prechorus_lead.ogg
new file mode 100644
index 0000000..0b0d4e2
--- /dev/null
+++ b/docs/music/img/early_sunsets_over_monroeville/prechorus_lead.ogg
Binary files differ
diff --git a/docs/music/img/early_sunsets_over_monroeville/prechorus_plucks.ogg b/docs/music/img/early_sunsets_over_monroeville/prechorus_plucks.ogg
new file mode 100644
index 0000000..88f9699
--- /dev/null
+++ b/docs/music/img/early_sunsets_over_monroeville/prechorus_plucks.ogg
Binary files differ
diff --git a/docs/music/img/index/ibanez.jpg b/docs/music/img/index/ibanez.jpg
new file mode 100644
index 0000000..4ba6ed6
--- /dev/null
+++ b/docs/music/img/index/ibanez.jpg
Binary files differ
diff --git a/docs/music/img/index/squier.jpg b/docs/music/img/index/squier.jpg
new file mode 100644
index 0000000..0d228e7
--- /dev/null
+++ b/docs/music/img/index/squier.jpg
Binary files differ
diff --git a/docs/music/img/uncure-your-depression/album-cover.jpg b/docs/music/img/uncure-your-depression/album-cover.jpg
new file mode 100644
index 0000000..53af91f
--- /dev/null
+++ b/docs/music/img/uncure-your-depression/album-cover.jpg
Binary files differ
diff --git a/docs/music/img/uncure-your-depression/ardour-editor.png b/docs/music/img/uncure-your-depression/ardour-editor.png
new file mode 100644
index 0000000..7f53907
--- /dev/null
+++ b/docs/music/img/uncure-your-depression/ardour-editor.png
Binary files differ
diff --git a/docs/music/img/uncure-your-depression/bass-chorus1.ogg b/docs/music/img/uncure-your-depression/bass-chorus1.ogg
new file mode 100644
index 0000000..b1d7c5e
--- /dev/null
+++ b/docs/music/img/uncure-your-depression/bass-chorus1.ogg
Binary files differ
diff --git a/docs/music/img/uncure-your-depression/bass-chorus2.ogg b/docs/music/img/uncure-your-depression/bass-chorus2.ogg
new file mode 100644
index 0000000..e4d1f0f
--- /dev/null
+++ b/docs/music/img/uncure-your-depression/bass-chorus2.ogg
Binary files differ
diff --git a/docs/music/img/uncure-your-depression/bass-solo.ogg b/docs/music/img/uncure-your-depression/bass-solo.ogg
new file mode 100644
index 0000000..971dee7
--- /dev/null
+++ b/docs/music/img/uncure-your-depression/bass-solo.ogg
Binary files differ
diff --git a/docs/music/img/uncure-your-depression/fuzz-guitar.ogg b/docs/music/img/uncure-your-depression/fuzz-guitar.ogg
new file mode 100644
index 0000000..29885fb
--- /dev/null
+++ b/docs/music/img/uncure-your-depression/fuzz-guitar.ogg
Binary files differ
diff --git a/docs/music/img/uncure-your-depression/lmms-editor.png b/docs/music/img/uncure-your-depression/lmms-editor.png
new file mode 100644
index 0000000..b180691
--- /dev/null
+++ b/docs/music/img/uncure-your-depression/lmms-editor.png
Binary files differ
diff --git a/docs/music/img/uncure-your-depression/lmms.ogg b/docs/music/img/uncure-your-depression/lmms.ogg
new file mode 100644
index 0000000..5f584af
--- /dev/null
+++ b/docs/music/img/uncure-your-depression/lmms.ogg
Binary files differ
diff --git a/docs/music/img/uncure-your-depression/looptober-v1.ogg b/docs/music/img/uncure-your-depression/looptober-v1.ogg
new file mode 100644
index 0000000..4d5190d
--- /dev/null
+++ b/docs/music/img/uncure-your-depression/looptober-v1.ogg
Binary files differ
diff --git a/docs/music/img/uncure-your-depression/looptober-v2.ogg b/docs/music/img/uncure-your-depression/looptober-v2.ogg
new file mode 100644
index 0000000..f97c03b
--- /dev/null
+++ b/docs/music/img/uncure-your-depression/looptober-v2.ogg
Binary files differ
diff --git a/docs/music/img/uncure-your-depression/solo-bassline.ogg b/docs/music/img/uncure-your-depression/solo-bassline.ogg
new file mode 100644
index 0000000..fce9d84
--- /dev/null
+++ b/docs/music/img/uncure-your-depression/solo-bassline.ogg
Binary files differ
diff --git a/docs/music/img/uncure-your-depression/tswuyd-20231031.ogg b/docs/music/img/uncure-your-depression/tswuyd-20231031.ogg
new file mode 100644
index 0000000..485d99d
--- /dev/null
+++ b/docs/music/img/uncure-your-depression/tswuyd-20231031.ogg
Binary files differ
diff --git a/docs/music/index.md b/docs/music/index.md
new file mode 100644
index 0000000..58cddb2
--- /dev/null
+++ b/docs/music/index.md
@@ -0,0 +1,101 @@
+# Music
+
+I used to play:
+
+- drums
+
+Now I play:
+
+- bass
+- ukulele
+- a midi keyboard, if you even consider it an instrument
+
+<details markdown="1">
+
+<summary>Click to expand infodump on how I got into music</summary>
+
+I was not raised a music person. As a young kid I had near zero exposure
+to formal music education. We would listen to Mandopop and Cantopop, and
+mom would hum tunes while she did dishes, but that was it.
+
+There was once my parents picked up a 88-key keyboard for me at our local
+supermarket in hope I might amount to something. I never did. Sorry mom
+and dad :(
+
+In sixth grade I joined a brass band as a percussionist (that was before
+I wore braces; the conductor looked at my teeth and said "nyeh"). For most
+of my band career I was in charge of the bass drum and the drum kit, but
+I did learn the timpani and a lil bit. My favorite performance however was
+Takarajima, where I played the agogo (the soul of this musical number,
+despite being just one bar over and over again).
+
+As a side effect, a disadvantage I had to all the other band kids was that
+I can't really read pitch, only rhythm. I did not know what a Bb minor
+was.
+
+Fast forward to 2021. The summer I graduated high school, I decided to
+challenge myself to learn ukulele. My father got one secondhand for me. It
+turned out you only need a tiny set of "easy" chords to get started. You
+can sing in other keys with a capo. And if that doesn't work, the song
+probably isn't made for the ukulele anyway.
+
+On my mom's birthday I sang her "House Of Gold". It remains my favorite
+song to play on ukulele.
+
+The uke sounds pretty, sure, but I'm afraid my hands are too fat to press
+adjacent frets (like D and Dm), and especially bar chords (B, Bm, etc). My
+setlist effectively stopped expanding as of 2022.
+
+So near the end of winter break, early 2023, I took the other extreme and
+invested in a bass (known in guitar world as long boi). It is a red Ibanez
+GSR200 PJ bass, the only model that'll ship in 3 days. The date of arrival
+is 2023-02-02.
+
+![Body of a red bass with stickers of Konqi, twenty one pilots, "Blåhaj
+Simp" and "This Machine Kills Homophobes"](img/index/ibanez.jpg)
+
+I learned most of my songs from [Rod
+Nieder](https://www.youtube.com/@rodnieder).
+
+However only less than a week later I found out I was somehow admitted
+into UMich. Afraid that the airline might wreck it, I decided not to take
+it with me across the ocean. Instead, I will buy a Squier that costs about
+the same. (I know it makes no real difference, but I kinda prefer the
+Telecaster-style headstock that has all pegs on one side.)
+
+!["Charcoal frost metallic" (grey and black) PJ bass in mint
+condition](img/index/squier.jpg)
+
+</details>
+
+## My music
+
+Here is a list of music I made, most recent first.
+
+### [This Song Will Uncure Your Depression](uncure-your-depression.md)
+
+![Album cover. Glass with residues of glued flyers. Title "this song will
+uncure your depression" and artist "Frederick Yin" handwritten in
+beige](img/uncure-your-depression/album-cover.jpg)
+
+My first attempt at actual composition. Involves synthesized equipments,
+bass, rapping, singing, and samples.
+
+### Choker
+
+I recorded this with my friend Ryan in August 2023 but I haven't got
+around to writing a blogpost
+
+### [Early Sunsets Over Monroeville](early-sunsets-over-monroeville.md)
+
+![Editor window in Ardour DAW](img/early_sunsets_over_monroeville/editor.png)
+
+I recorded the ukulele, did the vocals, and mixed them in Ardour. Result
+is an acoustic cover of "Early Sunsets Over Monroeville".
+
+## Not my music
+
+- [Review of My Chemical Romance discography](mcr_discog_review.md)
+- [Playlist to put on on my deathbed](deathbed_playlist.md)
+- [How I Discover a Band](band_discovery.md)
+- [Review of _Clancy_](clancy_review.md)
diff --git a/docs/music/mcr_discog_review.md b/docs/music/mcr_discog_review.md
new file mode 100644
index 0000000..84fe3b8
--- /dev/null
+++ b/docs/music/mcr_discog_review.md
@@ -0,0 +1,138 @@
+# Review of My Chemical Romance discography
+
+Review of MCR discography, in the same order I listened to each album from
+start to finish for the first time. Expect diminishing neutrality.
+
+## The Black Parade
+
+First listen: late 2020 or early 2021, I don't remember
+
+General remarks: Formed my first impression of MCR: monochrome pop punk
+talking about death. Or living in pain. Never got the point of the Patient
+as deeply as the real fans do.
+
+Fav track: Sleep
+
+Close runners-up: This Is How I Disappear, Mama (these are two tracks if
+you're unaware)
+
+Track stuck in head for longest: Disenchanted
+
+## Three Cheers For Sweet Revenge
+
+First listen: June 2022
+
+General remarks: Another concept album but this time I got the idea. In
+fact just go to the Amazon HQ and start killing. There's never a shortage
+of evil men. Sounds homogeneous genre-wise (not necessarily a bad thing).
+
+What I like: Dynamics — angelic hums in Interlude, then immediately the
+heavy guitars in Thank You For The Venom. The time change in INTYWIDFAL.
+Also, only two songs are named after a chorus line.
+
+Fav track: I Never Told You What I Do For A Living
+
+Fav line: And we all dance along to the tune of your death (I, for one, am
+certaining anticipating the tune of the death of certain people whose
+names my lawyer advised me not to elaborate)
+
+Close runner-up: The Ghost Of You
+
+Track stuck in head for longest: Cemetery Drive
+
+## Danger Days
+
+First listen: August 2022
+
+General remarks: _Another_ concept album? This time the lore goes as far
+as comic books, which I never read. To summarize, band fights evil
+corporation with laser weapons.
+
+What I like: Lots of mixed genres this time, and I'm glad they took
+a break from dark sounds.
+
+What I don't: This time 9 songs are named the easy way.
+
+Fav track to sing: Summertime (only song playable on ukulele)
+
+Fav line: Terrfied of what I'd be / as a kid from what I've seen / every
+single day when people try / and put the pieces back together / just to
+smash them down
+
+Fav track to listen to: Bulletproof Heart (extremely relatable)
+
+Track stuck in head for longest: The Kids From Yesterday
+
+## I Brought You My Bullets, You Brought Me Your Love
+
+First listen: September 2022
+
+General remarks: As with most debut albums, it didn't grow on me until
+a couple listens. I'm in a phase where I'd pick select tracks to listen to
+at will.
+
+January 2023 update: now I put on the entire record daily.
+
+What I like: Tempo changes! I wish they had more of them in later albums.
+Also the way Drowning Lessons has an outro, and Demolition Lovers is
+broken in half.
+
+What I don't: One, it's underproduced; two, Gerard's lines often consist
+of a bunch of half notes or quarter notes in succession (example: all but
+the last word in this line: We walk in single file / we light our rails
+/ and punch our time / ride escalators colder than a cell), and eight
+words in, this begins to sound monotonic.
+
+Fav track: Demolition Lovers (this started all the Three Cheers lore
+didn't it)
+
+Close runner-up: Early Sunsets Over Monroeville
+
+Remarks on ESOM: This song goes HARD. The first verse is all fun and games
+like "late dawns and early sunsets" and "holding hands and life was
+perfect" but by chorus it was shit like "before I pull this trigger" and
+"there's a corpse in my bed". Can't wait to recommend to people I hate.
+
+Track stuck in head for longest: also Demolition Lovers
+
+Misheard lyrics: "dressed in red and blue ice cream" where it should be "I
+squeezed" and "ice cream and roses fall at your feet" where it should be
+"rice grains"
+
+## Conventional Weapons
+
+First listen: October 2022
+
+General remarks: Well, I sure am glad MCR shelved these songs in favor of
+what is now Danger Days.
+
+Conventional Weapons is like No Phun Intended by Tyler Joseph in many
+ways, including how they reused some of its lyrics.
+
+What I like: Gerard's voice is crystal clear in many tracks.
+
+What I don't: Too much repetition, and there are artifacts of monotonic
+quarter notes from Bullets. Also some lyrics sound rhymed for rhymes'
+sake.
+
+Fav track: The World Is Ugly
+
+Misheard lyrics: "I wanna suicide, get my gun" where it should be "As soon
+as I get my gun". Truly horrifying misunderstanding.
+
+## Bonus mention: The Foundations of Decay
+
+First listen: May 2022
+
+General remarks: Just imagine how the real fans reacted. First song in
+8 years?! I hear it's categorized "progressive rock", but because the only
+other progressive rock song I've ever heard is Bohemian Rhapsody, the only
+conclusion I can draw is that all progressive rock songs are exactly
+6 minutes long.
+
+What I like: This song leaves a _lot_ to be anticipated. It's like
+a fragment of a map. Let's wait till MCR releases their fifth album.
+
+What I don't: "And so he gets to die a saint but she will always be the
+whore" ← like, _which_ she? This sounds sus out of context (I say this
+because I don't get the context).
diff --git a/docs/music/uncure-your-depression.md b/docs/music/uncure-your-depression.md
new file mode 100644
index 0000000..f5c8930
--- /dev/null
+++ b/docs/music/uncure-your-depression.md
@@ -0,0 +1,323 @@
+# This Song Will Uncure Your Depression
+
+2023-10-31
+
+[Take Me To The Song](#tswuyd-final-export)
+
+Download The Song (still haven't settled on which CC license)
+
+- [.ogg, 2.6 MiB](https://fkfd.me/static/Frederick%20Yin%20-%20This%20Song%20Will%20Uncure%20Your%20Depression.ogg)
+- [.wav, 16 bit, 48 kHz, 36.7 MiB](https://fkfd.me/static/Frederick%20Yin%20-%20This%20Song%20Will%20Uncure%20Your%20Depression.ogg)
+
+## Motivation
+
+I enrolled in a hip hop songwriting course and need something for the
+final project. I also happened to participate in #looptober on <abbr
+title="short for fediverse">fedi</abbr> for the first time.
+
+On 2023-10-14, I came up with some beats, chords and a guitar solo,
+despite me not able to play guitar irl:
+
+<audio controls src="../img/uncure-your-depression/looptober-v1.ogg"></audio>
+
+It was fire so i added a couple more things
+
+<audio controls src="../img/uncure-your-depression/looptober-v2.ogg"></audio>
+
+The sample you hear at the beginning is recorded by me as I landed at DTW,
+Detroit, to begin my two years of study at UMich. I also added some pitch
+bend to the guitar.
+
+## Jamming Time
+
+On 2023-10-17, I recorded a bassline for the solo. This version is
+recorded at 0.5x and sped up because I invented the bassline literally 10
+minutes ago. In the final product however I made it 1:1.
+
+<audio controls src="../img/uncure-your-depression/solo-bassline.ogg"></audio>
+
+There's also this distorted fuzzy guitar sound:
+
+<audio controls src="../img/uncure-your-depression/fuzz-guitar.ogg"></audio>
+
+Of course, this is no real guitar. It's actually my bass with effects in
+[Guitarix](https://guitarix.org).
+
+I am genuinely pleased by myself. It is the first thing I'm proud of since
+the omurice I cooked on september 24.
+
+At this point it's getting serious. I'm actually thinking about making
+this a full song. Especially when my hip hop professor got sick and asked
+us to submit something online, which seems like the perfect chance to
+drain every joule of energy into this song. It's healthy I swear.
+
+Long story short, this is the thing that came out of LMMS on 2023-10-27:
+
+<audio controls src="../img/uncure-your-depression/lmms.ogg"></audio>
+
+![LMMS editor window. 19 tracks in total.](img/uncure-your-depression/lmms-editor.png)
+
+Meanwhile, I took some training to use a studio we have in the library.
+I made a reservation on Saturday morning, 2023-10-28.
+
+## Lyrics Time
+
+It is Friday evening. I just realized I don't have the lyrics.
+
+Prior to writing, I made several yes's and no's for myself.
+
+- don't be passive aggressive.
+- don't blame innocent people.
+- don't overuse STEM metaphors.
+- don't make people suicidal.
+- do acknowledge your vulnerability.
+- do make the lyrics genuine and relatable.
+- do allow double takes and liberal interpretations.
+- do misspell a word just to sound good.
+
+What do I need? Well, four verses, one prechorus, one chorus, and one
+bridge. (Spoiler: I couldn't come up with anything for the bridge.) Just
+need to throw in eighth notes and triplets until they rhyme.
+
+Did I stick to the rules? [Well…](#appendix-i-lyrics)
+
+<audio id="tswuyd-final-export" controls src="../img/uncure-your-depression/tswuyd-20231031.ogg"></audio>
+
+What you are listening to is the final product, produced on 2023-10-31.
+
+I did:
+
+- acknowledge my vulnerabilities
+- make lyrics genuine
+- misspell words
+- use computer jargon once
+- allow interpretations
+
+Actually, seven lines were taken from my "museum of garbage". It's a note
+on my phone. When my brain's garbage facility fabricates a sentence that
+sounds like a lyric, I put it here.
+
+However, it seems I have failed to not be passive aggressive. Everything
+I wrote sounds negative. This is sadly very common in bands I drew
+inspiration from, such as Linkin Park. I will try to make it more positive
+next time.
+
+## Studio Time
+
+Ironically I missed the bus from my apartment to the library, so I had to
+wait twenty minutes for the next one. Anyway, I got in the studio on time.
+There is a Focusrite Scarlett interface, which had a whopping four inputs.
+I only need one though. The mic is Shure SM-something, and it's not bad.
+The Scarlett is plugged into the Mac on the desk and is not supposed to
+rewired. I rewired it anyway.
+
+I would like to thank PipeWire and people who do kernel stuff for handling
+audio flawlessly. But amdgpu crashed twice.
+
+The studio had the exact same headphones I use at home which is nice.
+
+After a two hour session my throat hurts, but it's impressive I had enough
+productivity to finish this.
+
+## Bass Time
+
+We're back from the studio! Heading back home to record bass. Although I'm
+not good at using a pick, I did it anyway for the punch.
+
+See [appendix](#appendix-iii-bass-tabs) for tabs and audio
+
+## Mixing Time
+
+I do not want to go into details because it's all guesswork. I *guess*
+that's loud enough. I *guess* that's enough reverb. I do *not* enjoy
+mixing audio. I had a version on 2023-10-28 which I *thought* could work,
+but turned out too quiet. I had to use another whole bunch of compression
+to get it as loud as possible while not raping your ears.
+
+![Ardour editor window. There are 19 tracks total.](img/uncure-your-depression/ardour-editor.png)
+
+## Album Art
+
+![See description below](img/uncure-your-depression/album-cover.jpg)
+
+The background is the glass wall of the Central Campus Transit Center bus
+stop, the busiest place on campus. It was plastered with posters related
+to Israel-Palestine days prior, then scrubbed, leaving heavy residue.
+I took this picture on my way back from the studio. Incidentally, I saw
+the first election poster this year, on the back of the wall (not
+pictured). I feel this photo captures the theme of the song.
+
+## Reflections
+
+I like this song as a concept. I really put my musicussy into it, and it's
+as good as I can possibly achieve.
+
+I would rate the music 4 stars out of 5. The guitar solo is obviously the
+single best thing throughout, but the bass and drums are just as good.
+Some room for improvement:
+
+- More spicy chords
+- Variable tempo
+- Time changes
+- The bridge sounds out of place
+
+Lyrically, I would rate it 3.5 stars. While I will not reveal the intended
+meaning, it reflects a period of cultural shock and unease as I adapt to
+a new environment. As said earlier I have fallen into the "rant about
+everything" trap, completely neglecting the positives in life. However one
+thing's for sure: I have surpassed Green Day's latest single, ["The
+American Dream Is Killing Me"](https://www.youtube.com/watch?v=t1TDvy7djJg).
+
+Vocal is not my forte, so I'm not gonna be overcritical of myself.
+However, in my next song I could work on putting more emotion into the
+rapping.
+
+## Appendix I. Lyrics
+
+[Verse 1]
+Due to unfortunate circumstances, I'm in Detroit
+Fourteen hours in the air my legs don't feel great
+The violent vibration is worsening my condition
+There's no way I'm already out of UTC+8
+
+[Verse 2]
+I never ask for help, the things under my belt
+The shame when I depend, the pride let me do it again
+But when I close my eyes, when I cuddle my blåhaj
+I wish it was someone, someone make me warm inside
+
+[Prechorus]
+Why am I not getting what I deserve?
+You said the grades won't be on a curve
+Look at my algo it's big O(no)
+Well in fact never mind now I see why
+I'd rather spend a night with Turing
+Than the silicon I'm abusing
+And the patience I'm losing
+Shut it down, you see we're cruising
+
+[Chorus]
+Will you be
+At the party after another divide?
+I can't afford
+The dawn's early light, oh yeah
+
+[Verse 3]
+I woke up late and I missed the bus (fuck)
+I guess that just what happens when you don't have a car
+The more I learned, the harder it become
+Your self esteem-ate only goes so far
+
+[Verse 4]
+I'm walking (I'm walking)
+I'm walking by the willow trees and watching (I'm watching)
+Watching out for the stupid geese
+Und ich kann davon erzählen, wie die Schmerzen fangen an
+因为我在太平洋另一面三万三刀学费的地方辗转流浪
+
+[Prechorus]
+
+[Chorus]
+
+[Bridge (Instrumental)]
+
+[Chorus]
+
+## Appendix II. Chord progression
+
+[Intro, Verses]
+Am G Am Dm (6x)
+
+[Prechorus]
+Am G Em Am Bdim
+Am G Em F
+
+[Chorus]
+Am G Am Dm
+Am G C Am
+
+[Solo]
+Am C Bdim Am
+C Dm Am Bdim
+
+[Bridge]
+Am C Bdim Am
+C Dm Am Am
+Am C Bdim Am
+C Dm Am Esus4
+
+(I'm not actually sure. I kinda butchered around)
+
+[Outro]
+Am C Bdim Am
+C Dm Am C
+
+## Appendix III. Bass tabs
+
+[Chorus 1]
+
+<audio controls src="../img/uncure-your-depression/bass-chorus1.ogg"></audio>
+
+```
+G --------|--------|--------|--------|--------|--------|--------|--------|
+D --------|--------|--------|0-0---0-|--------|--------|--------|--------|
+A 0---0--0|--------|0---0--0|--------|0---0--0|--------|3---3--3|0-0-----|
+E --------|3-3---3-|--------|--------|--------|3-3---3-|--------|--------|
+```
+
+[Chorus 2 & 3] (first seven bars are the same as 1)
+
+<audio controls src="../img/uncure-your-depression/bass-chorus2.ogg"></audio>
+
+```
+ 8
+G |--------|
+D |2-0-----|
+A |----3-0-|
+E |--------|
+```
+
+[Solo]
+
+<audio controls src="../img/uncure-your-depression/bass-solo.ogg"></audio>
+
+```
+G --------|--------|--------|--------|--------|--------|--------|--------|
+D --------|--------|----0033|22220---|--------|--------|--------|--------|
+A 0000----|--------|--22----|-----330|00000000|22222233|--------|--------|
+E ----0-00|001-3333|11------|--------|--------|--------|0000000 |111 1---|
+ \3_/ \3_/
+```
+
+## Appendix IV. Musical influences
+
+Lyrics mostly inspired by Linkin Park, Bill Wurtz and twenty one pilots
+
+"The dawn's early light" line came up while singing Green Day's "21st
+Century Breakdown" in the shower
+
+Rapping style inspired by Linkin Park and twenty one pilots
+
+Guitar solo at 2:50 unintendedly sounds like "Thank You For The Venom" by
+My Chemical Romance
+
+## Appendix V. Tools Used
+
+### Software
+
+- [LMMS](https://lmms.io): synth instruments
+- [Ardour](https://ardour.org): recording and production
+- [Guitarix](https://guitarix.org/): virtual amp
+- [Krita](https://krita.org/): album cover
+
+### Hardware (Studio)
+
+- Focusrite Scarlett 18i8
+- Shure SM-48
+- Audio-Technica ATH-M20x
+
+### Hardware (Home)
+
+- Behringer UMC22
+- Squier Affinity PJ bass
+- Audio-Technica ATH-M20x
diff --git a/docs/projects/bash-workshop.md b/docs/projects/bash-workshop.md
new file mode 100644
index 0000000..2a936af
--- /dev/null
+++ b/docs/projects/bash-workshop.md
@@ -0,0 +1,340 @@
+# bash workshop
+
+2023-03-09
+
+On two consecutive Sunday evenings (2023-02-26 and 2023-03-05) I hosted
+the bash workshop on behalf of <abbr title="tech department under JI
+student union">TechJI</abbr>.
+
+![I am standing in front of a projector screen in an
+auditorium](img/bash-workshop/workshop.jpg)
+
+▲ Me delivering the talky part of the workshop
+
+## Topics
+
+Despite its name, much of this workshop is not about bash alone, but other
+tools as well. Here is a brief list:
+
+### Part 1
+
+- Intro
+ * Brief history
+ * Unix philosophy
+- Files & File Tree
+ * `cat, cp, mv, rm`
+ * `cd, pwd, ls, mkdir`
+ * Paths, `.`, `..`, `*`
+- CLI
+ * Options and arguments
+ * Reading man pages
+ * Keyboard shortcuts
+- Pipes
+ * stdin, stdout
+ * `>` and `>>`
+ * Pipes
+ * `grep`
+
+### Part 2
+
+- Regex (ERE)
+ * Patterns
+ * `grep -E`
+ * `sed -E 's///'`
+ * Capturing groups
+- Scripting
+ * Variables
+ * `if`
+ * `for`
+
+## Goal
+
+I've been on Linux for 5 years. It would be a shame if I kept all that
+bash knowledge to myself. I believe bash is something that every
+computer-related major student should know, for the following reasons:
+
+1. Efficiency. Bash has saved hours if not days of my time and will save
+ yours as well.
+
+2. Portability. Literally every server you may ssh into will have a shell
+ of some sort, and having a hint of how it works will surely help.
+
+Therefore, the aim of bash workshop is
+
+1. Learn commands/scripts/tricks to boost efficiency
+
+2. Make the most out of GNU coreutils and a few common tools you'd find
+ anywhere
+
+## Materials used
+
+- Slides (2x, ~50 pages each totalling 1.5k lines of LaTeX)
+- Cheatsheet (A4 sheet handed out on site)
+- Tarball (Follow-along examples and DIY materials)
+
+Participants were asked to download a zip file containing all three.
+
+## Design
+
+Work began in October 2022. I scraped together all the topics I feel
+confident sharing with others, and then kicked out what I felt wasn't
+common enough. For example `tr` and `cut`.
+
+In November 2022 I approached Prof. Manuel Charlemagne with an early draft
+to ask if he had any advice. The draft was not yet split in half; it was
+a 71-page giant mess. His advice was to
+
+- Pack less in one workshop
+- Create fun activities to engage participants
+- Remove that distracting navbar on the bottom
+
+![A bar of buttons](img/bash-workshop/navbar.png)
+
+▲ Navbar in the default LaTeX beamer theme
+
+I decided that the workshop should follow a practicality-first approach,
+so I filled the slides with examples that's like, "yeah you'll have to
+figure this out sooner or later, so I'll show you how right now".
+
+![Slide screenshot](img/bash-workshop/environment_variables.png)
+
+▲ Slide in section 5, "Bash Scripting"
+
+<details markdown="1">
+<summary>Transcript</summary>
+Environment variables
+
+Let's say you're downloading something from a completely legal website,
+and you want the traffic to go through your completely legal local proxy
+for completely legal reasons.
+
+```
+# set environment variable for local proxy
+$ export HTTPS_PROXY=http://localhost:8080/
+# download the thing
+$ curl -O https://legal.website/legal-thing
+```
+</details>
+
+I also designed DIY sessions at the end of each section where I ask every
+participant to run some commands, design a regex, write a script, etc.
+
+![Slide screenshot](img/bash-workshop/cli_challenge.png)
+
+▲ Challenge session at the end of section 2, "CLI"
+
+<details markdown="1">
+<summary>Transcript</summary>
+Challenge
+
+- Read the man page for head
+- Experiment with files in `02-cli/`
+- Find a command to generate the following:
+
+```
+==> p0.txt <==
+MANIFESTO OF THE COMMUNIST PARTY.
+==> p1.txt <==
+I. BOURGEOIS AND PROLETARIANS.
+```
+
+Solution
+
+```
+$ head -n1 -v p0.txt p1.txt
+```
+</details>
+
+By the way, the navbar can be removed with
+
+```
+\setbeamertemplate{navigation symbols}{}
+```
+
+### Cheatsheet
+
+The cheatsheet is a compilation of commands, regexes and syntax I covered.
+
+![Part of cheatsheet](img/bash-workshop/cheatsheet.png)
+
+▲ I spent 6 RMB on 30 copies. 13 were handed out on site.
+
+### Easter eggs
+
+![Slide screenshot](img/bash-workshop/bash_history.png)
+
+▲ On top of practicality I tried my best to sprinkle some humor.
+
+<details markdown="1">
+<summary>Transcript</summary>
+A brief history of bash
+
+- Born: 1989
+- Probably played Pokémon on the Game Boy
+- Is an umbrella term for zsh, fish, …
+- Runs on Unix-like environments
+</details>
+
+![Slide screenshot](img/bash-workshop/ultimate_challenge.png)
+
+▲ The final DIY challenge at the very end
+
+<details markdown="1">
+<summary>Transcript</summary>
+The Ultimate Challenge
+
+Each line in `05-scripting/obf.txt` begins with an 8-digit number.
+Print every line with its number greater than any of the lines above.
+
+```
+IFS=$'\n'
+max=0
+for line in $(cat obf.txt); do
+ num=$(echo $line | grep -oE '^[0-9]{8}')
+ if [[ $num -gt $max ]]; then
+ echo $line
+ max=$num
+ fi
+done
+```
+</details>
+
+Now, with so much said and done, be prepared for the best trolling in
+TechJI's history.
+
+<details markdown="1">
+<summary>You probably won't be surprised…</summary>
+![ASCII art of Rick Astley](img/bash-workshop/rickroll.png)
+</details>
+
+## Advertising
+
+We needed to advertise our event to get people to come. I wrote the first
+draft on 2023-02-16, which begins with the place, time, topics and
+prerequisites, followed by the actual marketing.
+
+I ask readers to imagine themselves as an intern who received a message
+from their boss:
+
+"And here's some simpler work for you… ssh into the server, backup the
+logs in `/var/log`, and grep the error codes generated within one minute
+past each hour, then deduplicate them and…"
+
+The intern panics, for they never touched bash other than the bare minimum
+to pass <abbr title="CS intro course">VG151</abbr>. Panic turns into
+regret — that they failed to show up on our workshop.
+
+And thus the manipulation is done; reader is brainwashed to participate.
+
+(No, of course they aren't.)
+
+![Bash workshop: A Quarter Century of Unix](img/bash-workshop/wechat.png)
+
+▲ Header banner of WeChat article
+
+Two more revisions finalized the article on 2023-02-22. My venue
+reservation was approved the next day, but not yet confirmed via email, so
+I delayed posting.
+
+By Saturday, 2023-02-25, I realized it was Too Fucking Late™ and urgently
+sent the article anyway. However I was told we can't because WeChat limits
+our account to only one batch of articles each day, and it's already out
+there.
+
+(TechJI used to have its own account, but after some reformation all
+departments share one account, JISU.)
+
+This means we have to advertise for a Sunday event — on Sunday. The
+article dropped at 10 o'clock.
+
+Lesson learned: never pretend to play safe when you hear the clock
+ticking.
+
+## How it went
+
+### Attendance
+
+\# of participants who showed up in person are 10 and 4, respectively, for
+the two parts.
+
+4 was a bit low but given the abundance of competing events that night
+(there was an art festival and a guitar concert which, had there been no
+bash workshop, I would be vibing to). However, this meant a high
+attention-per-person.
+
+### Timing
+
+Part 1 lasted around 1.5 hr; part 2 lasted 2.5. Part of the 2.5 was
+troubleshooting one by one.
+
+In retrospect I should have kicked out `sed` and capturing groups. This
+will eliminate one DIY session which I admit is a bit overwhelming for
+those who heard the word "regex" for the first time.
+
+### Unexpected technical problems
+
+- Accessing WSL filesystem might be tricky in Windows
+- One person trapped themselves in vim
+- bash refuses to execute scripts with `\r\n` linebreaks
+- On MacBooks, the default bash is FreeBSD flavor and parses `{01..05}` as
+ `{1..5}`
+
+## Reflection
+
+Here is a list of things I will fix if I were to do it again.
+
+I will test the scripts on macOS;
+
+I will prepare the promotional article sooner so it gets published at
+least three days before the event, not nine hours;
+
+I will recap the conventions from part 1 so they don't confuse
+participants in part 2;
+
+![edited xkcd](img/bash-workshop/average_familiarity.png)
+
+▲ [Original: "Average Familiarity"](https://xkcd.com/2501/)
+
+<details markdown="1">
+<summary>Transcript</summary>
+Two stick figures have a conversation.
+
+"The command line is second nature to us Linux users, so it's easy to
+forget that the average person probably only knows how to use coreutils
+and one or two regexes."
+
+"And tar cvf of course."
+
+"Of course."
+
+Caption: Even when they're trying to compensate for it, experts in
+anything wildly overestimate the average person's familiarity with their
+field.
+</details>
+
+I will clarify where participants should `cd` into before they run
+commands;
+
+I will emphasize the difference between:
+
+- `var` and `$var`
+- `echo $str` and `cat file`
+- `echo $str | COMMAND` and `COMMAND $str`
+
+## Conclusion
+
+If workshops were children this would be my firstborn. To build a workshop
+from scratch is gratifying, especially when the topic is an extract of my
+five years of experience. I would like to thank Prof. Manuel Charlemagne;
+the feedback was very useful.
+
+I learned how to weigh things — to tell things that matter from those that
+don't — and how to make it worth the time for both friends and strangers.
+
+However, I need to rework my strategy toward event preparation. I should
+have pushed forward more aggressively. Few problems are worse than running
+out of time. There was no need to play safe.
+
+```
+$ ~ exit
+```
diff --git a/docs/projects/bikeblinkers.md b/docs/projects/bikeblinkers.md
index 8727fa8..de6d9c0 100644
--- a/docs/projects/bikeblinkers.md
+++ b/docs/projects/bikeblinkers.md
@@ -417,4 +417,4 @@ Oh no, Fred. You are _not_ letting that dreaded Feature Creep kick in.
__HEll YEs i Totally Am__
-Next episode: [Byseekel](../byseekel)
+Next episode: [Byseekel](byseekel.md)
diff --git a/docs/projects/blobcat-pcb.md b/docs/projects/blobcat-pcb.md
new file mode 100644
index 0000000..841e752
--- /dev/null
+++ b/docs/projects/blobcat-pcb.md
@@ -0,0 +1,171 @@
+# blobcat PCB
+
+2023-07-06
+
+![Yellow PCB standing, colorful LEDs in marquee mode. Printed on PCB:
+"TechJI 2023" and "owo :blobcat:"](img/blobcat-pcb/marquee.gif)
+
+▲ There are ten more LEDs on the back.
+
+Fun? Yes. Stupid? 100%. This is my second attempt at purely artistic PCBs
+(the first failed).
+
+TechJI is the organization I work in since 2021. I will retire from
+director this month, so I'm designing this as a farewell gift.
+
+Repo: [blobcat-pcb on Codeberg](https://codeberg.org/fkfd/blobcat-pcb)
+
+Oh, yes, and this makes a great SMT soldering exercise for beginners
+because all the parts are nowhere near out of stock (I don't know who'll
+die first — me or NE555).
+
+## Artistic design
+
+![Board outline](img/blobcat-pcb/outline.png)
+
+Board outline is based on [this svg of
+:blobcat_paw:](https://github.com/DuckOfDisorder/BlobCats/blob/main/SVGs/BlobCats/BlobCat_Paw.svg).
+
+I would have printed the pattern in black, if black silkscreen wasn't 10x
+more expensive.
+
+## Structural design
+
+You can snap the board in two and solder them up. The solder joints
+provide structural support and electrical conduction from batteries to
+circuitry. I took the idea from a project I saw earlier, but forgot which.
+
+## Logic design
+
+I <s>referenced</s> copied [FrostAutumn's design on LCSC
+OSHWHub](https://oshwhub.com/frostautumn/bie-zai-zhuo-li-fa-dian-v1-1).
+
+- NE555 in astable mode provides clock signal for CD4017
+- CD4017 outputs high level to exactly one LED
+- Each rising edge, a different LED turns on
+- Effect: marquee
+- Speed adjustable with RV1
+
+![Schematic for NE555 and CD4017](img/blobcat-pcb/logic.png)
+
+## Power design
+
+At first I wanted to use only one CR2032, just like FrostAutumn's design.
+Datasheet says NE555 needs 4.5V, so I'll need a boost converter. With the
+converter comes a lot of annoying stuff, like inductors, so I just said
+"fuck it" and threw away the converter idea.
+
+Now the board is powered by two CR2032s.
+
+## DIY Manual
+
+<details markdown="1">
+<summary markdown="1">__If you want to build your own, expand this__</summary>
+
+### BOM
+
+All R, C, and D are 0603.
+
+- U1: [NE555 SOIC-8](https://item.szlcsc.com/729488.html)
+- U2: [CD4017 SOP-16](https://item.szlcsc.com/331800.html)
+- R1: 2.2k (anything between 1k to 5k should work)
+- R2: 10k (or somewhere near that)
+- RV1: [500k](https://item.szlcsc.com/139843.html)
+- C1: 10nF
+- C2: 1uF
+- R10-19: 120 (120 to 180 is safe)
+- D10-19: LEDs, arbitrary colors, note that each reference is two LEDs
+- SW1: [SPDT, 6 pins](https://item.szlcsc.com/3178990.html)
+- BT1,2: No idea what the package is, I just happen to have some
+
+### Fabrication
+
+The fab may charge extra because they might mistake the battery board for
+a separate design. I negotiated out of paying for it.
+
+### Assembly
+
+When soldering, use flux generously.
+
+- Snap board into main board and battery board
+- File down the four snapped bits
+- Solder main board (the side with more parts first)
+- Solder battery board (make sure BT1,2 don't jiggle)
+- Rotate battery board by 90 degress
+- Insert into main board
+- solder 4 pairs of copper strips at crossing
+- Insert CR2032 batteries
+- Turn on SW1
+
+#### Note on LEDs
+
+LEDs typically have a green marking on the front side. It is the cathode.
+On the PCB, a U shape surrounds the LED. The valley is the cathode. It
+appears more yellow because it is connected to the ground plane.
+
+You should use the same color for each pair, because they share the same
+voltage. If you solder red and blue, only red will light up, because it
+requires less voltage.
+
+### Troubleshooting
+
+- It doesn't work at all
+
+Probe voltage across VCC and GND. If too low, check soldering on BT1,2 and
+SW1. Make sure BT1,2 lay completely flat and don't jiggle.
+
+If still no, check soldering on U1,2, C1,2, R1,2 and RV1. Gently nudge
+pins with tweezers. Does it jiggle? If so, rework.
+
+- An LED doesn't work
+
+First, check polarity.
+
+If the LED on the other side works, check if they're the same color. If
+you're not sure, replace.
+
+If neither LED works, check the resistor (R10-R19) and U2.
+
+### Bugs
+
+#### Battery
+
+CR2032 batteries exceed lowest board outline, assembly tips slightly.
+
+When using v0.4.2, solder BT1,2 (battery holders) a few millimeters above footprint.
+
+Fixed in v0.4.3.
+
+#### Filled zones
+
+Thermal reliefs work too well. When soldering BT1,2 and copper strips, you
+need to be patient. Heat will dissipate into filled zones. __Do not touch
+the board surface__; it will scald you. Use a clamp if possible.
+
+#### Important!
+
+Do not put blobcat v0.4.2 on a conductive surface. It will short BT1.
+
+</details>
+
+## Dangers of overcomplicating
+
+In earlier iterations, I massively overcomplicated the project by:
+
+- routing 20 pairs of LEDs instead of 10
+- driving LEDs with BJTs for fear of frying CD4017
+
+I managed to route them, but it was a pain. What's worse, I discarded the
+design, which technically marks my efforts moot. All this work for
+nothing.
+
+I don't know what the complexity of routing N parts is, but it definitely
+is higher than O(N).
+
+Don't overcomplicate your projects, kids!
+
+![Copper layers of overcomplicated design](img/blobcat-pcb/v0.2-cu.png)
+
+Copper layers in ▲ v0.2; ▼ v0.4
+
+![Copper layers of current design](img/blobcat-pcb/v0.4-cu.png)
diff --git a/docs/projects/byseekel.md b/docs/projects/byseekel.md
new file mode 100644
index 0000000..ff1f73f
--- /dev/null
+++ b/docs/projects/byseekel.md
@@ -0,0 +1,380 @@
+# Byseekel
+
+2023-07-17
+
+## Foreword
+
+This blogpost will be a disappointment. A recollection of wasted efforts,
+a mausoleum of failed designs, a journal of regrettable choices, and
+a maker's acceptance of defeat.
+
+Oh, and it won't be proofread.
+
+Last year, I began building a draft of this blogpost, but never finished
+nor published. It was timestamped 2022-05-03. I wish I could write this
+one in the same cheerful tone, but I can't.
+
+The project started in December 2021. It was finished in July 2023.
+A rational question to ask is:
+
+__What the fuck happened??__
+
+## Bikeblinkers: a recap
+
+In September 2021 I broke my wrist in a cycling accident. It gave me an
+irrational obsession for cycling safety. So I designed this.
+
+![A pair of PCB-form blinkers in a plastic bottle, mounted at the rear of
+a bike. One of them is blinking.](img/byseekel/bikeblinkers.jpg)
+
+▲ This is the only photo I have.
+
+It's basically just a 555 timer in astable mode. Two design errors were
+made:
+
+- The switch is on the path to the LEDs, not the 555, so the chip is
+ always consuming power
+- The transistors are in common-collector mode, so they aren't bright
+ enough
+
+__I was not satisfied.__
+
+## Byseekel rev 0, 1, and 2
+
+December 2021, I went back home with all the Bikeblinker stuff. Finally
+I had time to make something better.
+
+I wanted a whole bunch more features, including
+
+- speedometer and odometer, using reed switch and wheel-mounted magnet
+- hazard light
+- braking indicator
+- remote-activated buzzer
+- an LCD display
+
+At that point, every one of these seemed possible. I smugly named the
+project "Byseekel" because that's how I hear Freddie sing "Bicycle race".
+
+Rev 0 is on perfboard. I don't even remember how painful it was to solder.
+Nevertheless, it worked. Photo on 2022-01-01.
+
+![Two perfboards with messy wiring](img/byseekel/rev0_back.jpg)
+
+▲ I don't know why I needed three voltage boosters.
+
+🎖️ Achievement made: spaghetti wires
+
+![Same two perfboards, front side up](img/byseekel/rev0.jpg)
+
+I used two microcontrollers (MCUs), because I didn't know how to reliably
+switch in and out of sleep mode. The smaller one is an ATmega ATtiny13A
+
+The LCD was intended to go on the handlebar. It was driven with a 4-bit
+parallel interface. I wrote the driver myself with the datasheet, so I was
+very proud. (It was very glitchy, though)
+
+🎖️ Achievement made: First LCD driver
+
+Starting from Rev 1, I gave in and just made PCBs. This one's taken
+2023-01-12.
+
+![PCB with a freshly soldered chip](img/byseekel/rev1.jpg)
+
+▲ I was proud of being able to solder SMT, but it wasn't a good idea.
+
+🎖️ Achievement made: First functioning SMT chip
+
+This board is basically just a single chip and many connectors and
+jumpers. These include
+
+- J1: barrel jack, but instead of 9 or 12, it's 5 volts
+- J2: SPI, to flash firmware into the chip
+- J3: an Ethernet port because I need exactly 8 wires (genius moment)
+- J4: screw connectors
+- J5: controls array of 4 relays to turn blinkers on and off
+- JP1: a jumper to disable the relays before programming, because they
+ share wires, and if I don't pull it out the relay goes "click clack"
+ nonstop
+- J6: I thought it'd be cool if I had a few "hackable" pins for future use
+- U2: radio receiver unit
+
+But I fucked up. It's impossible to install U2 and J3 at the same time,
+because U2 is wider than printed on the PCB. I had completely forgotten.
+
+Fixed in Rev 2. The only photo, 2023-01-18.
+
+![Soldered and wired PCB](img/byseekel/rev2.jpg)
+
+Also, in Rev 1 I forgot I needed a receptacle for the Ethernet cable. So
+I put that in Rev 2.
+
+![Rendering of a PCB with a breakout board](img/byseekel/rev2_jdb.png)
+
+▲ I didn't use mousebites so JDB did not charge me extra. I used a handsaw
+to break them apart. Would not do again.
+
+The main problem with Rev 0 to 2 is the relay. Though they make
+a satisfying noise, they are way too power hungry for a battery powered
+device.
+
+__I was not satisfied.__
+
+## Rev 3
+
+There are too many flaws with Rev 2. I decided to revamp the design
+completely.
+
+Problem: The relay is too power hungry.
+Solution: Use transistors.
+
+Problem: I can no longer afford more SMT MCUs.
+Solution: Use DIP.
+
+Problem: I want a marquee-style blinker.
+Solution: 5 outputs instead of 2.
+
+Problem: 4-bit parallel interface is unstable and wastes wires.
+Solution: I2C.
+
+Let's begin by introducing designs I didn't throw away.
+
+### Controller board
+
+I intentionally made it credit-card sized. This was in March 2022.
+
+![Front and back of PCB](img/byseekel/rev3.jpg)
+
+### Stalks
+
+![A pair of stalks. One has exposed leads](img/byseekel/stalks.jpg)
+
+Blinker stalks for electric motorbikes. They're very cheap. You just pull
+the lever to the left or right, or both to get hazard lights. Press down
+to reset.
+
+### LED strips
+
+Planned red for brakes also, but now only yellow for blinkers. Scraped off
+some epoxy and soldered wires to the common anode and five cathodes. The
+voltage booster converts 5V to 12V.
+
+![The end of an unsoldered LED strip](img/byseekel/led_strip.jpg)
+
+▲ Then I sealed it in transparent heat shrink tube for waterproof.
+
+### Radio module
+
+433MHz OOK radio receiver, controlled with a garage door remote. Can store
+and respond to four keys. Currently assigned three to:
+
+- Lock (beep-beep, goes to sleep mode)
+- FindMyBike™ (long beep)
+- FindMyBike™ Silent Mode (flash blinkers)
+
+### Buzzer
+
+It's just a piezo with two LEDs. It beeps when you give it 12V.
+
+![Anatomical view of buzzer](img/byseekel/buzzer.jpg)
+
+### Ethernet cable
+
+In my original design I needed a bundle of eight wires that runs the
+entire length of my bike. An ethernet cable is a bundle of eight wires.
+Genius idea.
+
+Now that I only need three, it's still a genius idea because it's cheap
+and so much easier to organize than three speaker wires.
+
+Now, we'll move on to designs that failed.
+
+## Failed designs
+
+Don't ask a Shanghainese what happened in 2022.
+
+Anyway, I stayed at home from March to June, without access to my bicycle
+or stuff like oscilloscopes. So many ideas worked on my workbench, but
+couldn't be tested.
+
+![Line sketch of two boards. Stemming from controller board: power bank,
+blinkers, braking lights, buzzer, reed. In between: ethernet cable.
+Control panel board: ctrl, stalk, LCD, brake](img/byseekel/feature_creep.png)
+
+In the end, they grew so complicated I had to throw them away.
+
+![braking lights, reed, ctrl, LCD, brake and the control panel are crossed
+out](img/byseekel/feature_creep_removal.png)
+
+2024-02-14 update: There was [another version of this blogpost that
+I scrapped and rewrote](byseekel_scrapped.md), where you may find some of
+these dead ideas and bad puns.
+
+### Speedometer & odometer
+
+My uni has a required 80km of jogging per semester, tracked by GPS to
+ensure you're neither too fast or too slow. We cheat it by cycling. Based
+on my knowledge, I can stick a magnet to the spoke of the wheel, and mount
+a reed switch somewhere so each time the magnet passes, we know the wheel
+made one turn.
+
+Problem is, it was winter break, and my bike wasn't home. So to test the
+mechanism, I just held the magnet in my hand and went "whoosh whoosh
+whoosh".
+
+Did it work? On paper, yes. In practice? Probably not.
+
+Then why did I attempt anyway? Perhaps it's because I hate jogging so
+much. My superinflated maker ego was like, "Company xxx made this so
+I must also be able to." Wrong. There are limits as to what one person can
+do at home, even if it "sounds possible".
+
+Right now, 80km is no longer a requirement for me, so I just dropped the
+idea, along with the LCD and control panel.
+
+### LCD
+
+It's a pity I cut it out. It almost worked.
+
+The LCD would serve as a HUD for:
+
+- speed in km/h and min/km
+- trip and total mileage (stored in EEPROM)
+- status of blinkers, brake, and hazard light
+
+I bought an I2C-to-4-bit adaptor board, and rewrote my driver (based on
+[Sovichea Tep's TWI driver](https://github.com/Sovichea/avr-i2c-library)).
+
+🎖️ Achievement made: First I2C LCD driver
+
+The LCD was less glitchy than in parallel mode. In parallel, when the GPIO
+switched, the voltage overshot before it damped down. Thanks to Kliment,
+I learned this was called ringing. But the dedicated circuitry for I2C
+damps harder, so there's less of this problem.
+
+Less, not none though.
+
+### Control panel
+
+One of the ideas that seem good on paper.
+
+![CAD rendering. A stack of boards with parts sandwiched
+in.](img/byseekel/panel_freecad.png)
+
+▲ Made with FreeCAD.
+
+Sometimes, you just feel too confident. You think you're a god. You think
+you can do anything, including mechanical engineering.
+
+The intention is to laser cut eight pieces of acrylic, and bolt them
+together. All seemed perfect.
+
+![Laser cut contours](img/byseekel/panel_laser.png)
+
+In November 2022 I tried to cut them. But the laser cutter didn't like the
+dxf exported from FreeCAD via Inkscape. Each edge was not one line but
+two, so the laser head went over each edge twice. RIP acrylic.
+
+![Acrylic with severely burnt edges](img/byseekel/panel_burnt.jpg)
+
+### Ctrl button
+
+The ctrl button was supposed to switch the HUD when pressed and enter
+sleep mode when held.
+
+### Brake
+
+The brake detection is the single least realistic design I've ever come up
+with. I can't believe how I let it pass. I won't waste time trying to
+explain it again. Here's a figure I drew last year. Go figure.
+
+![](img/byseekel/brakes_idea.png)
+
+## Dormant state and revival
+
+I did practically nothing for the project between October 2022 to March
+2023, because DON'T ASK A SHANGHAINESE WHAT HAPPENED IN 2022. Anyway, in
+April 2023 I found myself motivated once again, partly because there's
+a new hackerspace.
+
+I got this box in 2022. Now I'm finally doing things to it. Horrible,
+horrible things.
+
+![Left: a badly drilled hole on the box. Right: buzzer inserted to said
+hole.](img/byseekel/buzzer_drill.jpg)
+
+▲ I didn't have a suitable grinder, so I just drilled around until the
+small holes formed a big hole.
+
+![Left: the box put vertically in the basket; blinkers taped to basket
+rim. Right: Stalk taped to handlebar.](img/byseekel/basket_mount.jpg)
+
+▲ This made the bicycle difficult to steer, so later I moved the box to
+the back.
+
+## Byseekel, now
+
+What does Byseekel look like, now that I killed every feature I wasn't
+ready for?
+
+We've gone full circle.
+
+Bikeblinkers was blinkers you controlled with a stalk. Right now, Byseekel
+is essentially the same thing.
+
+Except:
+
+- better stalk
+- better wiring
+- powered by MCU
+- marquee
+- FindMyBike™
+- IP-somewhat waterproof
+
+Note that, although there's an ATmega328P, after all the features I killed
+it's essentially the same as your typical Arduino code.
+
+When I have photos and videos, I will post them here.
+
+### 2023-07-17 update
+
+![Left: interior of a box, containing a PCB on top of a lithium battery.
+Right: same box mounted to a bike](img/byseekel/assembly.jpg)
+
+▲ It's mounted with tape, and when it rained, I just cover the box with
+a plastic bag. Am I an engineer now?
+
+So far I've easily rode it around for more than 50 km. The tape seems to
+be holding it together pretty well, even on bumpy roads. The box has been
+exposed to hours of direct sunlight in 38C weather and rain that made the
+campus quite literally a water park and the lithium battery hasn't
+exploded or caught fire. This is all I ever wanted.
+
+### 2023-07-22 update
+
+<video controls><source src="../img/byseekel/wtf.mp4"></video>
+
+MPEG-4 Video (4.6 MiB)
+
+## Conclusion
+
+Whose fault is it that Byseekel wasn't finished until now?
+
+- It is my fault that I overcomplicated the project
+- It is not my fault that I was locked home for half a year
+
+I am now speaking to future self.
+
+Do you want to add a "useful feature" to a project? You gotta get the
+project working without it first. A new batch of PCBs cost 30 RMB. That's
+cheaper than hours of time you'd waste otherwise. Don't sit on your
+computer, working your ass to make your CAD model "perfect". You can't
+know what's perfect if you never test it.
+
+If I had another chance, I'd kill the brake and reed ideas. The LCD taught
+me to write I2C drivers, and to write AVR C with interrupts; I'd give it
+a pass.
+
+Always remember, do one thing and do it well.
+
+The KiCad and AVR C files for Byseekel and Bikeblinkers are on
+[Codeberg](https://codeberg.org/fkfd/byseekel).
diff --git a/docs/projects/byseekel_scrapped.md b/docs/projects/byseekel_scrapped.md
new file mode 100644
index 0000000..5ee7ef7
--- /dev/null
+++ b/docs/projects/byseekel_scrapped.md
@@ -0,0 +1,441 @@
+# Byseekel (scrapped version)
+
+> Note: This is the scrapped version of [Byseekel](byseekel.md). I began
+> writing this article in early 2022, until I abandoned it 2022-05-02. The
+> next year I revived and finished the project, but killed many features.
+> That was when I decided to rewrite this blogpost from scratch, with less
+> groundless optimism you'd find in this scrapped version.
+
+> Nevertheless, for archival reasons, I am publishing the scrapped version
+> on 2024-02-14. This article is not finished and will not be updated.
+> Please disregard any sentence that suggests otherwise.
+
+> I deleted the images a long time ago, so I put in the alt text instead.
+
+> Article begins here.
+
+Freddie Mercury famously sang:
+
+> I want to ride my bicycle I want to ride my bike
+> I want to ride my bicycle I want to ride it where I like
+
+I, too, own a bicycle, but in terms of performance and condition it is
+like crap. I certainly wouldn't take riding my bicycle as much of
+a pleasure as Mercury did. This makes me ask myself a question: What do
+I need to make a bike trip more enjoyable, or just less painful?
+
+A [cyclocomputer](https://en.wikipedia.org/wiki/Cyclocomputer).
+
+Image: Cateye cyclocomputer mounted to a bicycle handlebar.
+
+Image credit: Wikipedia user
+[Mnisawlpsa](https://commons.wikimedia.org/wiki/User:Mnisawlpsa).
+
+Despite its stupid name (cyclo? computer? smh) it's kinda useful. It keeps
+track of the speed and mileage of your bike, and displays them on
+a screen. I do know one application where it definitely fits, and that is
+cheating the 80-kilometers-of-jogging-per-semester mandate from my school
+office.
+
+Every (able) undergrad in my uni has to accumulate a certain jogging
+mileage by the end of each semester. For boys, the number is 80
+kilometers. To track your movement you turn on your phone's GPS and start
+jogging. But the loophole is, it can't actually tell if you're jogging, as
+long as you keep your speed within 3-9 minutes/km. Experiments show I am
+able to maintain a steady 3-5 min/km on a bike and trick the system into
+believing I'm legitimately jogging. But even so, I have to pull out my
+phone mid-journey — a quite dangerous act, even when no one was around
+— to make sure I'm not going too fast. It would be helpful if I had
+a device to check that for me and warn me if it exceeds that range.
+
+Guess what, it'd be extra helpful if it could tell me how far I've cycled.
+It'd also be cool if I could integrate this with my previous
+semi-successful project, [Bikeblinkers](bikeblinkers.md). What if besides
+blinkers, it also had a braking light. Hey, since looking for my bike
+among 200 others in front of the gate is such a pain, how about something
+that would tell me where it is?
+
+I should totally make a bucket list of features to implement:
+
+- speedometer
+- odometer
+- blinkers
+- hazard light
+- braking indicator
+- remote-activated buzzer
+
+And no, this is so much more than a cyclocomputer. As a tribute to Freddie
+Mercury, I'm going to name my project after how he enunciates the word
+"bicycle".
+
+Note: this blogpost is continuously being updated.
+
+## Hardware
+
+### MCU (spoiler: it's AVR)
+
+The project at hand really seems to dwarf [Bikeblinkers](bikeblinkers.md)
+in terms of complexity, and apparently a single 555 IC simply won't do,
+and an MCU is necessary.
+
+Which MCU? That is one of the most consequential decisions to make. Up to
+this point I had a really limited tech stack, because before this one all
+I knew was Arduino (in this blogpost I treat the Arduino as a distinct
+"MCU", despite its real MCU being ATmega328P), and I hadn't really done
+any project that (a) has an MCU, and (b) runs more than one meter away
+from a computer and a power source that draws power from the mains.
+
+"How about Arduino," I thought. However, upon close inspection, the
+Arduino isn't all cake.
+
+- Many Arduino libraries are bloat and inefficient (I'm not judging
+ though)
+- Arduino IDE v1 is a huge pain to use (Yes I am judging here)
+- The Arduino has a 3.3V regulator which isn't useful for this project but
+ consumes quite a substantial chunk of power
+
+That said, I am really hoping there was some alternative. What I want is
+a bare-bones version of Arduino, where I get the freedom to do whatever
+I want, but still somehow match the Arduino's capabilities.
+
+I have a few ATmega328P's lying around, so let's try them.
+
+🛒 BOM += ATmega328P
+
+Admittedly, you _can_ embed AVR C/C++ snippets in your `.ino` or
+completely write your sketch with it, and if done right it's possible to
+compile and download an Arduino sketch onto your average ATmega328P, but
+what's the fun? I'm going to ditch Arduino completely here.
+
+I'm going with avr-gcc and avr-libc, so that I won't touch assembly and as
+a bonus practice C which I had been learning for 2/3 of a semester.
+
+I fetched a copy of datasheet for the ATmega328P and began reading. The
+most important information as of now is
+
+- Pinout (GPIO and SPI)
+- IO registers (`PORTB`, `DDRB`, etc.)
+
+I bought a [USBASP](https://www.fischl.de/usbasp/) to download my code
+onto the chip. Well, in fact it's not the code I'm downloading, it's the
+hex file obtained by compiling the code, linking the libraries, generating
+an elf, then summoning Satan.
+
+There are three limitations on the specs one ought to care about when
+designing software for an AVR chip:
+
+- Flash size
+- SRAM size
+- Clock frequency
+
+The 328P has 32KB flash, 2KB SRAM and a maximum frequency of 20MHz, but of
+course we're not using all that much. Of the three what worries me most is
+RAM, because after 2/3 of a semester of C I know how hard keeping track of
+memory can be, and for some reason my programs easily ate up megabytes on
+the OJ. Although 2KB sounds extremely limited, fortunately we don't need
+fancy algorithms here.
+
+So far, all I needed was the I/O functionality, and the most basic
+conditionals and loops C has to offer. By lumping up stuff like
+
+```c
+#include <avr/io.h>
+#include <util/delay.h>
+int main(void) {
+ DDRB |= _BV(PB5); // set pin direction of PB5 to output
+ while (1) {
+ PORTB ^= _BV(PB5); // toggle PB5
+ _delay_ms(500);
+ }
+}
+```
+
+I was able to blink an LED.
+
+Image: An illuminating LED driven by a DIP-28 ATmega328P on a breadboard
+
+This was all very basic, equivalent to what you would do with `pinMode`
+and `digitalWrite` on an Arduino. Although I was eager to write some
+_real_ code, I should figure out how my MCU would sense and react to the
+physical world.
+
+### Input Peripherals
+
+#### Learn to reed
+
+To calculate the speed the bicycle is traveling at, we need to count the
+number of revolutions. I initially had two ideas:
+
+- Optical: reflective material on the spokes, IR LED and phototransistor
+ on the fork;
+- Magnetic: reed switch on the fork and a permanent magnet on the spokes.
+
+The optical solution in theory works like this:
+
+Image: Diagram showing the location and schematics of the optical sensor
+
+However, the weaknesses are pretty obvious.
+
+- Requires power source
+- Requires directional alignment
+- Requires a minimum of three wires (VCC, GND, output)
+- Aluminum foil on a bicycle wheel looks silly
+- Electrical noise
+
+Moreover, the industrial standard for cyclocomputers is reed switches.
+
+Image: Wired reed switch sensor with spoke mounted magnet.
+
+Image credit: Wikipedia user
+[AndrewDressel](https://en.wikipedia.org/wiki/User:AndrewDressel).
+
+They should be installed on the rear wheel, because rotations of the rear
+wheel are more representative of the real mileage.
+
+🛒 BOM += reed switch, permanent magnet
+
+#### Turning big
+
+In Bikeblinkers my control for the blinker circuit was a tiny SP3T toggle
+switch. It's a bit too small, and it was the source of two design errors
+in my rev. 1 PCB (look I gotta find someone else to blame okay?)
+
+Image: A SP3T toggle switch mounted on a PCB
+
+🛒 BOM += bigger SP3T switch
+
+I spent a fortune buying one, but it was worth it. Cue the industrial
+switch!
+
+Image: Industrial SP3T switch in metal and plastic casing. It is thrown to the right.
+
+(ruler in centimeters)
+
+This is all good and such, but one problem is it's pretty likely you'll go
+all the way to the opposite direction when your intention is to throw it
+back to neutral. No countermeasure so far.
+
+In February 2022, I came up with an alternative. This is one of those
+blinker switches they use on e-bikes. It has three terminals: left, right,
+and common. Flick it to the left, and left connects to common. Flick it to
+the right, and right does so. Push the stalk down to disconnect all
+terminals. Two models are sold: one that allows three terminals to be
+simultaneously connected, and one that doesn't. I picked the former, as
+hazard lights are just left and right blinkers on at the same time.
+
+🛒 BOM += e-bike blinker switches
+
+Image: A pair of e-bike blinker switches. One of them has its three copper
+terminals exposed.
+#### Brake it apart
+
+The brake is a little trickier and as of the time of writing I have one
+idea but have absolutely no idea how it would work out in the physical
+world.
+
+Image: Diagram of the location and schematics of the brake switches
+
+This design taps into movements of the brake handle but not anywhere
+further in the braking mechanism, e.g. brake pads. This is because brakes
+are a key part to bike safety, and it's stupid to tamper it just for
+a braking indicator.
+
+It comprises of four metal contacts: two on the left, two on the right. Of
+each two, one is on the hinge and remains stationary relative to the
+bicycle, but the other is somewhere on the moving part, the handle. They
+are normally closed, forming an electrical connection. When you pull back
+the handle to engage the brake though, they are forced apart, breaking the
+continuity. The two pairs are wired in series and act like a single
+switch. When it is open, it means at least one brake is engaged, which is
+good enough since we don't care which.
+
+Sure, ugly hack, but having read Arduino forum threads like
+[this](https://forum.arduino.cc/t/interfacing-with-a-bicycle-sensing-braking-solved/72661)
+it actually isn't _the_ ugliest one, while in my reach in terms of
+afforability.
+
+Problems:
+
+- I can't think of a way to install them other than superglue
+- Rainwater can interfere with the connection, for example shorting a pair
+ of contacts that are supposed to be open
+- The location of contacts have to be super precise
+- Exposed wires are a potential cosmetic imperfection (shut up your bike
+ isn't beautiful anyway)
+
+🛒 BOM += tiny metal contact plates? is that a thing?
+
+📋 TODO: install this to prove I'm not an idiot
+
+#### WhereTheFuck™
+
+WhereTheFuck™ (WTF) is a bleeding-edge, innovative and disruptive
+technology which augments and accelerates your vehicle retrieving
+experience with audiovisual cues.
+
+OK, it's just a regular bike finder. The components are an RF receiver and
+a transmitter at 433MHz, and a LED-beeper combo, which I will show later.
+
+🛒 BOM += RF TX/RX kit
+
+I am not a radio expert, so what I bought is this kit. It's a cute
+[OOK](https://en.wikipedia.org/wiki/On%E2%80%93off_keying) RF receiver
+module capable of detecting four programmable signals, which happens to be
+the number of keys the transmitter has. The transmitter is just your
+average garage door opener.
+
+Image: Left to right: four-key RF transmitter with antenna extended, RF
+receiver as a small yellow PCB with 7 bent pin headers.
+
+Looks like I can attach an antenna to the RX module too, so here's a bunch
+of them I ordered a few weeks later.
+
+Image: 10 RF antennae in plastic packaging. They look like coils with tails and
+are made of copper.
+
+📋 TODO: install, find out how effective it is
+
+### Output Peripherals
+
+#### Crystal clear
+
+Sure, we're able to calculate our speed and mileage now, but of course we
+will need to display it somehow. For this purpose we will use an LCD.
+I have one lying around but it's one of those expensive 12864 models,
+which is best for complex graphics and a complete overkill for showing
+a bunch of numbers. So I opted for a downgrade, i.e. 1602, which can only
+draw characters only (of course, there are hacks which let you draw custom
+pixmaps. we won't touch them here)
+
+🛒 BOM += 1602 LCD
+
+Image: An LCD with soldered pins
+
+#### Blinkers, again (also stop lamp)
+
+On one hand, I could reuse the blinkers for my previous project,
+Bikeblinkers. They are working perfectly fine; they're just four sets of
+yellow LEDs. I even spent hours designing a 3D printed case, which in the
+end was never gestated into the physical world, but I feel it would be
+a pity if no one saw it, so here's a picture.
+
+Image: FreeCAD model of an enclosure for blinkers and a stop lamp
+
+However I want something big. No, not physically, but it's going to amaze
+everyone.
+
+🛒 BOM += LED strips
+
+Look at these bright boys!
+
+Image: 1 meter long red LED strip resting on my lap. It's drawing 654mA at
+12.0V from my DC power supply and hella bright
+
+I bought a red strip for the stop lamp and a yellow one for blinkers, one
+meter each which is a plenty amount. Given I cut them in appropriate
+lengths, they don't draw much more power than the discrete LEDs do.
+
+#### WhereTheFuck™
+
+My idea for WhereTheFuck™ is, I press a button on my transmitter, and
+something on my bicycle flashes and/or makes a loud noise. That is
+basically my patent claim. How novel.
+
+I bought this thingie, which is basically two LEDs and a (really loud)
+buzzer, and it fits in a 22mm hole designed for industrial pushbuttons.
+When fed 12V it happily beeps on and off thanks to its internal RC
+circuitry (it's visible from the holes).
+
+Image: Buzzer. It's a black plastic cylinder with a transluscent red cap.
+
+Image: The red lid is removed to reveal two LEDs and a buzzer.
+
+## Software
+
+### Oh boy, avr-gcc
+
+That's right y'all, it's official: I wrote C for AVR. Embedded
+programming. One more line on my resume I guess?
+
+Oh, you're asking me if it's enjoyable? You better ask yourself: is it
+really possible to enjoy C? Is it in all plausibility an attainable
+outcome that I, a mere mortal of flesh and soul, would have the `__asm__
+__volatile__` in my command, or possess enough power to conquer the army
+of CMOS logic gates? No. To write C one must suffer. To control the
+silicon, one must first wrestle it, then tame it.
+
+#### State and global variables
+
+Global variables are gross, but atop 2,048 bytes of memory there are no
+rules. I am representing global state in a pile of global variables. One
+of them is called `state`. At the time of writing it holds 5 boolean
+states in bits [6..2] (imagine the memory saved compared to 5 ints) and
+the two least significant bits represent a finite state of four.
+
+```c
+uint8_t state = 0;
+```
+
+As you might have expected, reading and writing on `state` is a pain. But
+AVR C is full of bitwise stuff anyways, so it doesn't really matter if you
+define macros wisely.
+
+In my `main.h` I defined the following:
+
+```c
+#define BRAKE_BIT _BV(6)
+#define BLINKER_L_BIT _BV(5)
+#define BLINKER_R_BIT _BV(4)
+// ...
+```
+
+(`_BV(x)` is macro for `1 << x` which is used to create a bitmask, with BV
+standing for Bit Value)
+
+To test a bit, say `BRAKE_BIT`, I need to do:
+
+```c
+if (state & BRAKE_BIT) { /*...*/ }
+```
+
+And to alter it:
+
+```c
+state |= BRAKE_BIT; // set to one
+state &= ~BRAKE_BIT; // set to zero
+```
+
+#### LCD
+
+#### Stop lamp
+
+Despite the hardware implementation being unproven, this is one of the
+easiest features in software. There are only two possibilities:
+
+- braking, and
+- not braking
+
+If it is the former, we turn on the red stop lamp. Otherwise, turn it off.
+Here's the code:
+
+```c
+void braking(void) {
+ if (PIND & BRAKES && !(state & BRAKE_BIT)) {
+ state |= BRAKE_BIT;
+ PORTC |= STOPLAMP;
+ lcd_print("[BR]", 0x19);
+ } else if (!(PIND & BRAKES) && state & BRAKE_BIT) {
+ state &= ~BRAKE_BIT;
+ PORTC &= ~STOPLAMP;
+ lcd_clear(0x19, 4);
+ }
+}
+```
+
+...where `STOPLAMP` is a macro for `_BV(PC1)`, the assigned pin.
+
+#### Blinker
+
+#### Sleep mode
+
+#### WhereTheFuck™
diff --git a/docs/projects/fkfdme.md b/docs/projects/fkfdme.md
new file mode 100644
index 0000000..39b2865
--- /dev/null
+++ b/docs/projects/fkfdme.md
@@ -0,0 +1,191 @@
+# fkfd.me
+
+First published 2023-11-27
+
+Latest version 2023-11-27
+
+Yes — the inevitable "How I built my blog" blogpost! Except it's like the
+44th post, and hey, at least it's not Jekyll or Hexo or WordPress (no
+offense).
+
+## Early history (2020)
+
+### fkfd.me
+
+I bought the domain fkfd.me on 2020-02-14 for 20.98 USD, after my previous
+domain, [autometalogolex](https://xkcd.com/1932/).me expired.
+
+fkfd.me was for my comics initially; my blog was hosted on blog.fkfd.me.
+
+### The meaning behind fkfd
+
+fkfd is a contraction of fakefred.
+
+fakefred, which happens to be my GitHub username, stems from the facts
+that (a) I go by the English name Frederick and (b) there was an "X might
+be fake" internet meme in mainland China.
+
+In retrospect, fkfd is a genuinely high-quality name. All four letters are
+in the QWERTY home row. And I'm glad I didn't go for fakefred.me because
+that would be cringe.
+
+### First design
+
+The first git commit was made on 2020-04-23.
+
+I opted for MkDocs as the static site generator, forked the
+[alabaster](https://mkdocs-alabaster.ale.sh/) theme, and called it
+alabaster-lite. I disabled all JavaScript, kicked out all the clutter, and
+left this masterpiece:
+
+![Website titled "You are in the wrong place. Leave." followed by
+a condescending paragraph demanding the reader to leave my
+blog](img/fkfdme/2020-04-23.png)
+
+### Background color
+
+If you are reading in dark theme, you'll notice that the background color
+hasn't changed at all. It is `#19202b`.
+
+Why this particular color? I don't know. Probably forgot. No comment in
+the CSS either. This kind of shit is why I keep a blog, to archive my
+motivations.
+
+## Then it was like this for one year and a half (2022)
+
+Per `git log`, the next big update was in 2022-01-07. I graduated high
+school, and began to use fkfd.me as a legit blog, moving comics to
+fkfd.me/comics. I also reorganized all past blogposts (like 7 at the time)
+into categories (projects, random, shitpost).
+
+![Title "fkfd". Body: "Yes this is just a low effort blog why do you
+ask"](img/fkfdme/2022-01-07.png)
+
+### Categories
+
+Two days later I did a major redesign on the homepage.
+
+![Title "fkfd". Headings: "Categories", "Non-blog Content", "Fun", "All
+Blogposts"](img/fkfdme/2022-01-09.png)
+
+A few months later I joined [Fediring](https://fediring.net) and increased
+the line spacing. But apart from that not so much changed.
+
+## Fast forward 7 months (2022-2023)
+
+### Bad design. Just no
+
+On 2022-12-28 I was at home with covid. It really messes with your head.
+Which explains this absolute disaster of a design:
+
+![Every category is a solid colored block with a handdrawn icon. They form
+a 2x3 grid.](img/fkfdme/2022-12-28.png)
+
+It would have looked great, if my theme was more vibrant, which it is not.
+
+Effort, however, is not wasted. The grid layout has been reused ever
+since.
+
+### Much better
+
+Half a month later, 2023-01-15:
+
+![The solid color blocks are gone](img/fkfdme/2023-01-15.png)
+
+Two days later:
+
+![Updated icons for ham radio and shitpost, and new icons for
+fediring](img/fkfdme/2023-01-17.png)
+
+### About Me
+
+I also included my About Me page in the heading, because I find it the
+most useful page when I'm visiting someone else's blog. What's interesting
+about you? What do you enjoy? What do we have in common? If it warrants
+a whole ass page, it deserves a heading unto itself.
+
+## Another 7 months (2023)
+
+### New categories
+
+In summer 2023 I flew to the US to study at U of Michigan, thus the UMich
+category. There was only one music post, initially in Projects (namely
+[Early Sunsets Over
+Monroeville](../music/early-sunsets-over-monroeville.md)), but I did
+anticipate more coming in the future, so I made it a category of its own.
+Turns out a great decision because of [This Song Will Uncure Your
+Depression](../music/uncure-your-depression.md).
+
+![Categories section is now 2x4 grid](img/fkfdme/2023-09-03.png)
+
+### Blogposts I don't regret
+
+The "Blogposts I don't regret" section was actually added in Janurary too.
+I didn't include it in my screenshot because then you won't be able to see
+my Fediring icons 🪐🚀
+
+It was a compromise between "Featured blogposts" (narcissist) and
+"Blogposts that don't suck" (self-deprecating). I don't care about _your_
+opinion. _I_ find them representative of my style, and thus good "entry
+points" for a new visitor.
+
+### Accessibility
+
+Around this time I grew more and more accessibility-aware. A few problems
+were fixed, such as text contrast and heading levels.
+
+### This is not the right vibe
+
+I've visited dozens of personal websites, but few do I consider
+"excellent". An "excellent" website is consistent in content and style,
+but manages to surprise me.
+
+For example, [maia crimew](https://maia.crimew.gay/)'s website delivers
+all its promises:
+
+- soft kitten
+- disregard of authority
+- gay
+
+It lowkey disappoints me when someone's website fails to capture their
+energy. I feel like if you are gay, a furry, a pirate, or a gay furry
+pirate, you most certainly deserve a decent website to show off that
+energy. Not that techbro-style boilerplate.
+
+Unfortunately, my blog falls right into this category. As a remedy,
+I would try to add an essential element of any good website:
+
+__Cat.__
+
+Thus on 2023-11-05:
+
+![Three icons on top of page, including a contour of a cat
+head](img/fkfdme/2023-11-05.png)
+
+It is a link to my About Me page, which does mention my affinity to cats.
+
+### A more subdued approach
+
+At this point, there are three sets of icons on my homepage:
+
+- header icons (about, x, links)
+- categories
+- fediring
+
+Categories are supposed to be the focus, and the other two are stealing
+it. Also it looks terrible on mobile. Not good. I reverted the design only
+one week later.
+
+But the assets were still there. Plus, I really liked the cat icon.
+Meanwhile the sidebar is mostly empty. And there we go!
+
+![Four icons in sidebar: cat head (about me), radiating atom (atom feed),
+balls and sticks (links), ascii tilde (~x). Below are fediring
+icons](img/fkfdme/2023-11-27.png)
+
+### Hover
+
+I also made alternative icons for the categories that appear on hover. The
+problem is that, the browser does not prefetch them, and they flicker for
+a few milliseconds on the first hover. I can't find any non-hacky way to
+fix it.
diff --git a/docs/projects/idkhow-pcb.md b/docs/projects/idkhow-pcb.md
new file mode 100644
index 0000000..adc21bf
--- /dev/null
+++ b/docs/projects/idkhow-pcb.md
@@ -0,0 +1,91 @@
+# I DONT KNOW HOW BUT I MADE A PCB
+
+2024-03-23
+
+Last December I made a bold move to buy a ticket to iDKHOW's show in
+Detroit. The show is on April 5th.
+
+I've heard of fans giving out handmade souvenirs on shows like this. In my
+mind I have a PCB with the band logo on it. The logo happens to be super
+regular shapes like lines and arcs. As long as I don't do it for profit,
+I'm fine. So I imagine I'd be at the show just handing them out to people.
+
+## Step 0: What do I make and how?
+
+First idea: blinking LEDs like my previous PCB, [Blobcat](blobcat-pcb.md).
+
+Too many parts, too much soldering. No time for that when I've got 18
+credits of courses.
+
+Second idea: a coil that harvests power from your phone's NFC and light an
+LED.
+
+Much less soldering but physics is too hard. I can't even prove it's
+feasible.
+
+Third idea: just a logo and nothing else.
+
+Really easy to design but a waste of money if I pay a factory. But there's
+another option.
+
+There's a Bantam machine at my uni that any student can use as long as
+they passed a quiz. The Bantam is basically a computerized milling
+machine. Instead of dissolving copper with chemicals like they do in
+factories, it mechanically etches off copper with a drill bit.
+
+A common drill bit is 1/32 inch, or about 0.8 mm. This means I can't do
+fine traces. Not a problem.
+
+## Step 1: Design PCB
+
+I created a PCB with KiCad with only two useful layers:
+
+- F.Cu (front copper, where my design goes)
+- Edge.Cuts (board outline and holes)
+
+To minimize milling time, I need to maximize copper and minimize etching.
+So I fiddled around a bit and found such a design that looks ok:
+
+![KiCad PCB design measuring 36×26 mm with
+a hole](img/idkhow-pcb/kicad_pcb.png)
+
+The red curves and pale red solid fill are copper and between them is
+where the drill bit goes.
+
+## Step 2: Use the Bantam
+
+On 2024-03-20 at 18:00, I checked in at the machine shop. My friend guided
+me through the process and gave me his drill bit. The drill bit is
+carbide, but brittle at high speeds.
+
+![Bantam machine drilling an FR-1 board. My thumb rests on the emergency
+stop button.](img/idkhow-pcb/bantam.jpg)
+
+The drill bit rotates at a nerve-wrecking speed and copper shreds splashed
+everywhere (in the protective window, ofc). Each board took 9 minutes in
+the machine, plus some extra where I recalibrated the spindle. I had
+enough material for 9 of them, so it took around 2 hours.
+
+![Nine PCBs laid in a grid](img/idkhow-pcb/product.jpg)
+
+Note that #9 has a defect. Part of the trace wasn't etched deep enough.
+I sanded all of them but went too hard on #2 (row 2, column 1). It's kinda
+scratchy and I don't wanna risk sanding off everything. So I'm claiming it
+as my own and using it as a keyfob.
+
+![Seven PCBs laid in a stack and another one in
+front](img/idkhow-pcb/stack.jpg)
+
+Overall, this was really cool. I enjoy abusing public resources for my
+useless projects, and I will do it again.
+
+## Pre-show update
+
+2024-03-31 update: I was reminded there's a line in the opening track
+"DOWNSIDE":
+
+> And when I die I'll get those cold copper kisses on my eyes
+
+Therefore, I am from now on calling them "cold copper kisses".
+
+Despite the name, it is highly recommended that you do not kiss it.
diff --git a/docs/projects/img/bash-workshop/average_familiarity.png b/docs/projects/img/bash-workshop/average_familiarity.png
new file mode 100644
index 0000000..8b2c772
--- /dev/null
+++ b/docs/projects/img/bash-workshop/average_familiarity.png
Binary files differ
diff --git a/docs/projects/img/bash-workshop/bash_history.png b/docs/projects/img/bash-workshop/bash_history.png
new file mode 100644
index 0000000..4233fa6
--- /dev/null
+++ b/docs/projects/img/bash-workshop/bash_history.png
Binary files differ
diff --git a/docs/projects/img/bash-workshop/cheatsheet.png b/docs/projects/img/bash-workshop/cheatsheet.png
new file mode 100644
index 0000000..589f941
--- /dev/null
+++ b/docs/projects/img/bash-workshop/cheatsheet.png
Binary files differ
diff --git a/docs/projects/img/bash-workshop/cli_challenge.png b/docs/projects/img/bash-workshop/cli_challenge.png
new file mode 100644
index 0000000..ff251ee
--- /dev/null
+++ b/docs/projects/img/bash-workshop/cli_challenge.png
Binary files differ
diff --git a/docs/projects/img/bash-workshop/environment_variables.png b/docs/projects/img/bash-workshop/environment_variables.png
new file mode 100644
index 0000000..0f4b488
--- /dev/null
+++ b/docs/projects/img/bash-workshop/environment_variables.png
Binary files differ
diff --git a/docs/projects/img/bash-workshop/navbar.png b/docs/projects/img/bash-workshop/navbar.png
new file mode 100644
index 0000000..5c434e6
--- /dev/null
+++ b/docs/projects/img/bash-workshop/navbar.png
Binary files differ
diff --git a/docs/projects/img/bash-workshop/rickroll.png b/docs/projects/img/bash-workshop/rickroll.png
new file mode 100644
index 0000000..a418e6f
--- /dev/null
+++ b/docs/projects/img/bash-workshop/rickroll.png
Binary files differ
diff --git a/docs/projects/img/bash-workshop/ultimate_challenge.png b/docs/projects/img/bash-workshop/ultimate_challenge.png
new file mode 100644
index 0000000..344fcd6
--- /dev/null
+++ b/docs/projects/img/bash-workshop/ultimate_challenge.png
Binary files differ
diff --git a/docs/projects/img/bash-workshop/wechat.png b/docs/projects/img/bash-workshop/wechat.png
new file mode 100644
index 0000000..1ef1cf7
--- /dev/null
+++ b/docs/projects/img/bash-workshop/wechat.png
Binary files differ
diff --git a/docs/projects/img/bash-workshop/workshop.jpg b/docs/projects/img/bash-workshop/workshop.jpg
new file mode 100644
index 0000000..b3a6432
--- /dev/null
+++ b/docs/projects/img/bash-workshop/workshop.jpg
Binary files differ
diff --git a/docs/projects/img/blobcat-pcb/logic.png b/docs/projects/img/blobcat-pcb/logic.png
new file mode 100644
index 0000000..3f0728a
--- /dev/null
+++ b/docs/projects/img/blobcat-pcb/logic.png
Binary files differ
diff --git a/docs/projects/img/blobcat-pcb/marquee.gif b/docs/projects/img/blobcat-pcb/marquee.gif
new file mode 100644
index 0000000..008a1b0
--- /dev/null
+++ b/docs/projects/img/blobcat-pcb/marquee.gif
Binary files differ
diff --git a/docs/projects/img/blobcat-pcb/outline.png b/docs/projects/img/blobcat-pcb/outline.png
new file mode 100644
index 0000000..b2553f1
--- /dev/null
+++ b/docs/projects/img/blobcat-pcb/outline.png
Binary files differ
diff --git a/docs/projects/img/blobcat-pcb/v0.2-cu.png b/docs/projects/img/blobcat-pcb/v0.2-cu.png
new file mode 100644
index 0000000..5770872
--- /dev/null
+++ b/docs/projects/img/blobcat-pcb/v0.2-cu.png
Binary files differ
diff --git a/docs/projects/img/blobcat-pcb/v0.4-cu.png b/docs/projects/img/blobcat-pcb/v0.4-cu.png
new file mode 100644
index 0000000..fde782c
--- /dev/null
+++ b/docs/projects/img/blobcat-pcb/v0.4-cu.png
Binary files differ
diff --git a/docs/projects/img/byseekel/assembly.jpg b/docs/projects/img/byseekel/assembly.jpg
new file mode 100644
index 0000000..f6f78b1
--- /dev/null
+++ b/docs/projects/img/byseekel/assembly.jpg
Binary files differ
diff --git a/docs/projects/img/byseekel/basket_mount.jpg b/docs/projects/img/byseekel/basket_mount.jpg
new file mode 100644
index 0000000..8e53473
--- /dev/null
+++ b/docs/projects/img/byseekel/basket_mount.jpg
Binary files differ
diff --git a/docs/projects/img/byseekel/bikeblinkers.jpg b/docs/projects/img/byseekel/bikeblinkers.jpg
new file mode 100644
index 0000000..c33cec8
--- /dev/null
+++ b/docs/projects/img/byseekel/bikeblinkers.jpg
Binary files differ
diff --git a/docs/projects/img/byseekel/brakes_idea.png b/docs/projects/img/byseekel/brakes_idea.png
new file mode 100644
index 0000000..3601f9b
--- /dev/null
+++ b/docs/projects/img/byseekel/brakes_idea.png
Binary files differ
diff --git a/docs/projects/img/byseekel/buzzer.jpg b/docs/projects/img/byseekel/buzzer.jpg
new file mode 100644
index 0000000..9324228
--- /dev/null
+++ b/docs/projects/img/byseekel/buzzer.jpg
Binary files differ
diff --git a/docs/projects/img/byseekel/buzzer_drill.jpg b/docs/projects/img/byseekel/buzzer_drill.jpg
new file mode 100644
index 0000000..1e6e33c
--- /dev/null
+++ b/docs/projects/img/byseekel/buzzer_drill.jpg
Binary files differ
diff --git a/docs/projects/img/byseekel/feature_creep.png b/docs/projects/img/byseekel/feature_creep.png
new file mode 100644
index 0000000..45c0597
--- /dev/null
+++ b/docs/projects/img/byseekel/feature_creep.png
Binary files differ
diff --git a/docs/projects/img/byseekel/feature_creep_removal.png b/docs/projects/img/byseekel/feature_creep_removal.png
new file mode 100644
index 0000000..d82cc59
--- /dev/null
+++ b/docs/projects/img/byseekel/feature_creep_removal.png
Binary files differ
diff --git a/docs/projects/img/byseekel/led_strip.jpg b/docs/projects/img/byseekel/led_strip.jpg
new file mode 100644
index 0000000..1614649
--- /dev/null
+++ b/docs/projects/img/byseekel/led_strip.jpg
Binary files differ
diff --git a/docs/projects/img/byseekel/panel_burnt.jpg b/docs/projects/img/byseekel/panel_burnt.jpg
new file mode 100644
index 0000000..bb6d6fc
--- /dev/null
+++ b/docs/projects/img/byseekel/panel_burnt.jpg
Binary files differ
diff --git a/docs/projects/img/byseekel/panel_freecad.png b/docs/projects/img/byseekel/panel_freecad.png
new file mode 100644
index 0000000..f7c824b
--- /dev/null
+++ b/docs/projects/img/byseekel/panel_freecad.png
Binary files differ
diff --git a/docs/projects/img/byseekel/panel_laser.png b/docs/projects/img/byseekel/panel_laser.png
new file mode 100644
index 0000000..cb8211e
--- /dev/null
+++ b/docs/projects/img/byseekel/panel_laser.png
Binary files differ
diff --git a/docs/projects/img/byseekel/rev0.jpg b/docs/projects/img/byseekel/rev0.jpg
new file mode 100644
index 0000000..5027962
--- /dev/null
+++ b/docs/projects/img/byseekel/rev0.jpg
Binary files differ
diff --git a/docs/projects/img/byseekel/rev0_back.jpg b/docs/projects/img/byseekel/rev0_back.jpg
new file mode 100644
index 0000000..a3458a8
--- /dev/null
+++ b/docs/projects/img/byseekel/rev0_back.jpg
Binary files differ
diff --git a/docs/projects/img/byseekel/rev1.jpg b/docs/projects/img/byseekel/rev1.jpg
new file mode 100644
index 0000000..1b3731b
--- /dev/null
+++ b/docs/projects/img/byseekel/rev1.jpg
Binary files differ
diff --git a/docs/projects/img/byseekel/rev2.jpg b/docs/projects/img/byseekel/rev2.jpg
new file mode 100644
index 0000000..7850ec4
--- /dev/null
+++ b/docs/projects/img/byseekel/rev2.jpg
Binary files differ
diff --git a/docs/projects/img/byseekel/rev2_jdb.png b/docs/projects/img/byseekel/rev2_jdb.png
new file mode 100644
index 0000000..0f51367
--- /dev/null
+++ b/docs/projects/img/byseekel/rev2_jdb.png
Binary files differ
diff --git a/docs/projects/img/byseekel/rev3.jpg b/docs/projects/img/byseekel/rev3.jpg
new file mode 100644
index 0000000..0ff6e72
--- /dev/null
+++ b/docs/projects/img/byseekel/rev3.jpg
Binary files differ
diff --git a/docs/projects/img/byseekel/stalks.jpg b/docs/projects/img/byseekel/stalks.jpg
new file mode 100644
index 0000000..658a253
--- /dev/null
+++ b/docs/projects/img/byseekel/stalks.jpg
Binary files differ
diff --git a/docs/projects/img/byseekel/wtf.mp4 b/docs/projects/img/byseekel/wtf.mp4
new file mode 100644
index 0000000..7b8fc7c
--- /dev/null
+++ b/docs/projects/img/byseekel/wtf.mp4
Binary files differ
diff --git a/docs/projects/img/fkfdme/2020-04-23.png b/docs/projects/img/fkfdme/2020-04-23.png
new file mode 100644
index 0000000..1210be9
--- /dev/null
+++ b/docs/projects/img/fkfdme/2020-04-23.png
Binary files differ
diff --git a/docs/projects/img/fkfdme/2022-01-07.png b/docs/projects/img/fkfdme/2022-01-07.png
new file mode 100644
index 0000000..2877838
--- /dev/null
+++ b/docs/projects/img/fkfdme/2022-01-07.png
Binary files differ
diff --git a/docs/projects/img/fkfdme/2022-01-09.png b/docs/projects/img/fkfdme/2022-01-09.png
new file mode 100644
index 0000000..edfbc71
--- /dev/null
+++ b/docs/projects/img/fkfdme/2022-01-09.png
Binary files differ
diff --git a/docs/projects/img/fkfdme/2022-04-11.png b/docs/projects/img/fkfdme/2022-04-11.png
new file mode 100644
index 0000000..7c7d22e
--- /dev/null
+++ b/docs/projects/img/fkfdme/2022-04-11.png
Binary files differ
diff --git a/docs/projects/img/fkfdme/2022-05-07.png b/docs/projects/img/fkfdme/2022-05-07.png
new file mode 100644
index 0000000..ad49a6f
--- /dev/null
+++ b/docs/projects/img/fkfdme/2022-05-07.png
Binary files differ
diff --git a/docs/projects/img/fkfdme/2022-12-28.png b/docs/projects/img/fkfdme/2022-12-28.png
new file mode 100644
index 0000000..522eaa9
--- /dev/null
+++ b/docs/projects/img/fkfdme/2022-12-28.png
Binary files differ
diff --git a/docs/projects/img/fkfdme/2023-01-15.png b/docs/projects/img/fkfdme/2023-01-15.png
new file mode 100644
index 0000000..55521db
--- /dev/null
+++ b/docs/projects/img/fkfdme/2023-01-15.png
Binary files differ
diff --git a/docs/projects/img/fkfdme/2023-01-17.png b/docs/projects/img/fkfdme/2023-01-17.png
new file mode 100644
index 0000000..d949d9b
--- /dev/null
+++ b/docs/projects/img/fkfdme/2023-01-17.png
Binary files differ
diff --git a/docs/projects/img/fkfdme/2023-09-03.png b/docs/projects/img/fkfdme/2023-09-03.png
new file mode 100644
index 0000000..5939890
--- /dev/null
+++ b/docs/projects/img/fkfdme/2023-09-03.png
Binary files differ
diff --git a/docs/projects/img/fkfdme/2023-11-05.png b/docs/projects/img/fkfdme/2023-11-05.png
new file mode 100644
index 0000000..2bc9ed4
--- /dev/null
+++ b/docs/projects/img/fkfdme/2023-11-05.png
Binary files differ
diff --git a/docs/projects/img/fkfdme/2023-11-27.png b/docs/projects/img/fkfdme/2023-11-27.png
new file mode 100644
index 0000000..6b8d48c
--- /dev/null
+++ b/docs/projects/img/fkfdme/2023-11-27.png
Binary files differ
diff --git a/docs/projects/img/fkfdme/icons.png b/docs/projects/img/fkfdme/icons.png
new file mode 100644
index 0000000..15b6986
--- /dev/null
+++ b/docs/projects/img/fkfdme/icons.png
Binary files differ
diff --git a/docs/projects/img/idkhow-pcb/bantam.jpg b/docs/projects/img/idkhow-pcb/bantam.jpg
new file mode 100644
index 0000000..1004748
--- /dev/null
+++ b/docs/projects/img/idkhow-pcb/bantam.jpg
Binary files differ
diff --git a/docs/projects/img/idkhow-pcb/kicad_pcb.png b/docs/projects/img/idkhow-pcb/kicad_pcb.png
new file mode 100644
index 0000000..061ea47
--- /dev/null
+++ b/docs/projects/img/idkhow-pcb/kicad_pcb.png
Binary files differ
diff --git a/docs/projects/img/idkhow-pcb/product.jpg b/docs/projects/img/idkhow-pcb/product.jpg
new file mode 100644
index 0000000..d92d5da
--- /dev/null
+++ b/docs/projects/img/idkhow-pcb/product.jpg
Binary files differ
diff --git a/docs/projects/img/idkhow-pcb/stack.jpg b/docs/projects/img/idkhow-pcb/stack.jpg
new file mode 100644
index 0000000..d147eef
--- /dev/null
+++ b/docs/projects/img/idkhow-pcb/stack.jpg
Binary files differ
diff --git a/docs/projects/img/nand2tetris_1/6502.jpg b/docs/projects/img/nand2tetris_1/6502.jpg
new file mode 100644
index 0000000..06c6e7f
--- /dev/null
+++ b/docs/projects/img/nand2tetris_1/6502.jpg
Binary files differ
diff --git a/docs/projects/img/nand2tetris_1/alu.kra b/docs/projects/img/nand2tetris_1/alu.kra
new file mode 100644
index 0000000..a86fafa
--- /dev/null
+++ b/docs/projects/img/nand2tetris_1/alu.kra
Binary files differ
diff --git a/docs/projects/img/nand2tetris_1/alu.png b/docs/projects/img/nand2tetris_1/alu.png
new file mode 100644
index 0000000..64bed05
--- /dev/null
+++ b/docs/projects/img/nand2tetris_1/alu.png
Binary files differ
diff --git a/docs/projects/img/nand2tetris_1/alu_highlighted.png b/docs/projects/img/nand2tetris_1/alu_highlighted.png
new file mode 100644
index 0000000..3290f07
--- /dev/null
+++ b/docs/projects/img/nand2tetris_1/alu_highlighted.png
Binary files differ
diff --git a/docs/projects/img/nand2tetris_1/and_gate.png b/docs/projects/img/nand2tetris_1/and_gate.png
new file mode 100644
index 0000000..95a8ec6
--- /dev/null
+++ b/docs/projects/img/nand2tetris_1/and_gate.png
Binary files differ
diff --git a/docs/projects/img/nand2tetris_1/and_gate_nand.png b/docs/projects/img/nand2tetris_1/and_gate_nand.png
new file mode 100644
index 0000000..9af285a
--- /dev/null
+++ b/docs/projects/img/nand2tetris_1/and_gate_nand.png
Binary files differ
diff --git a/docs/projects/img/nand2tetris_1/and_gate_nand_not.png b/docs/projects/img/nand2tetris_1/and_gate_nand_not.png
new file mode 100644
index 0000000..e230230
--- /dev/null
+++ b/docs/projects/img/nand2tetris_1/and_gate_nand_not.png
Binary files differ
diff --git a/docs/projects/img/nand2tetris_1/computer.kra b/docs/projects/img/nand2tetris_1/computer.kra
new file mode 100644
index 0000000..72cab90
--- /dev/null
+++ b/docs/projects/img/nand2tetris_1/computer.kra
Binary files differ
diff --git a/docs/projects/img/nand2tetris_1/computer_registers.png b/docs/projects/img/nand2tetris_1/computer_registers.png
new file mode 100644
index 0000000..4999988
--- /dev/null
+++ b/docs/projects/img/nand2tetris_1/computer_registers.png
Binary files differ
diff --git a/docs/projects/img/nand2tetris_1/cpu.kra b/docs/projects/img/nand2tetris_1/cpu.kra
new file mode 100644
index 0000000..2022f55
--- /dev/null
+++ b/docs/projects/img/nand2tetris_1/cpu.kra
Binary files differ
diff --git a/docs/projects/img/nand2tetris_1/cpu.png b/docs/projects/img/nand2tetris_1/cpu.png
new file mode 100644
index 0000000..c29068f
--- /dev/null
+++ b/docs/projects/img/nand2tetris_1/cpu.png
Binary files differ
diff --git a/docs/projects/img/nand2tetris_1/cpu_book.png b/docs/projects/img/nand2tetris_1/cpu_book.png
new file mode 100644
index 0000000..2611a06
--- /dev/null
+++ b/docs/projects/img/nand2tetris_1/cpu_book.png
Binary files differ
diff --git a/docs/projects/img/nand2tetris_1/cpu_controls.png b/docs/projects/img/nand2tetris_1/cpu_controls.png
new file mode 100644
index 0000000..77c034a
--- /dev/null
+++ b/docs/projects/img/nand2tetris_1/cpu_controls.png
Binary files differ
diff --git a/docs/projects/img/nand2tetris_1/cpu_emulator_pong.png b/docs/projects/img/nand2tetris_1/cpu_emulator_pong.png
new file mode 100644
index 0000000..5dd9a75
--- /dev/null
+++ b/docs/projects/img/nand2tetris_1/cpu_emulator_pong.png
Binary files differ
diff --git a/docs/projects/img/nand2tetris_1/cpu_hunch.png b/docs/projects/img/nand2tetris_1/cpu_hunch.png
new file mode 100644
index 0000000..5c12596
--- /dev/null
+++ b/docs/projects/img/nand2tetris_1/cpu_hunch.png
Binary files differ
diff --git a/docs/projects/img/nand2tetris_1/cpu_mysterious_jump_logic.png b/docs/projects/img/nand2tetris_1/cpu_mysterious_jump_logic.png
new file mode 100644
index 0000000..a460fcd
--- /dev/null
+++ b/docs/projects/img/nand2tetris_1/cpu_mysterious_jump_logic.png
Binary files differ
diff --git a/docs/projects/img/nand2tetris_1/mux.png b/docs/projects/img/nand2tetris_1/mux.png
new file mode 100644
index 0000000..da7f3d2
--- /dev/null
+++ b/docs/projects/img/nand2tetris_1/mux.png
Binary files differ
diff --git a/docs/projects/img/nand2tetris_1/pc.png b/docs/projects/img/nand2tetris_1/pc.png
new file mode 100644
index 0000000..0cbbd20
--- /dev/null
+++ b/docs/projects/img/nand2tetris_1/pc.png
Binary files differ
diff --git a/docs/projects/img/nand2tetris_1/pc_incorrect.png b/docs/projects/img/nand2tetris_1/pc_incorrect.png
new file mode 100644
index 0000000..2ac3ae8
--- /dev/null
+++ b/docs/projects/img/nand2tetris_1/pc_incorrect.png
Binary files differ
diff --git a/docs/projects/img/nand2tetris_1/register.png b/docs/projects/img/nand2tetris_1/register.png
new file mode 100644
index 0000000..ff32d3c
--- /dev/null
+++ b/docs/projects/img/nand2tetris_1/register.png
Binary files differ
diff --git a/docs/projects/img/nand2tetris_1/register_internal.png b/docs/projects/img/nand2tetris_1/register_internal.png
new file mode 100644
index 0000000..f9ef91e
--- /dev/null
+++ b/docs/projects/img/nand2tetris_1/register_internal.png
Binary files differ
diff --git a/docs/projects/img/nand2tetris_1/zx_nx.png b/docs/projects/img/nand2tetris_1/zx_nx.png
new file mode 100644
index 0000000..14330a2
--- /dev/null
+++ b/docs/projects/img/nand2tetris_1/zx_nx.png
Binary files differ
diff --git a/docs/projects/img/nand2tetris_2.1/function_states.kra b/docs/projects/img/nand2tetris_2.1/function_states.kra
new file mode 100644
index 0000000..a92e83a
--- /dev/null
+++ b/docs/projects/img/nand2tetris_2.1/function_states.kra
Binary files differ
diff --git a/docs/projects/img/nand2tetris_2.1/function_states.png b/docs/projects/img/nand2tetris_2.1/function_states.png
new file mode 100644
index 0000000..b0276fb
--- /dev/null
+++ b/docs/projects/img/nand2tetris_2.1/function_states.png
Binary files differ
diff --git a/docs/projects/img/nand2tetris_2.1/function_states_blobs.png b/docs/projects/img/nand2tetris_2.1/function_states_blobs.png
new file mode 100644
index 0000000..db7472e
--- /dev/null
+++ b/docs/projects/img/nand2tetris_2.1/function_states_blobs.png
Binary files differ
diff --git a/docs/projects/img/nand2tetris_2.1/ram.kra b/docs/projects/img/nand2tetris_2.1/ram.kra
new file mode 100644
index 0000000..3d2c3f4
--- /dev/null
+++ b/docs/projects/img/nand2tetris_2.1/ram.kra
Binary files differ
diff --git a/docs/projects/img/nand2tetris_2.1/ram_areas.png b/docs/projects/img/nand2tetris_2.1/ram_areas.png
new file mode 100644
index 0000000..9650182
--- /dev/null
+++ b/docs/projects/img/nand2tetris_2.1/ram_areas.png
Binary files differ
diff --git a/docs/projects/img/nand2tetris_2.1/stack.kra b/docs/projects/img/nand2tetris_2.1/stack.kra
new file mode 100644
index 0000000..ca0dd1b
--- /dev/null
+++ b/docs/projects/img/nand2tetris_2.1/stack.kra
Binary files differ
diff --git a/docs/projects/img/nand2tetris_2.1/stack_after_add.png b/docs/projects/img/nand2tetris_2.1/stack_after_add.png
new file mode 100644
index 0000000..efc19e8
--- /dev/null
+++ b/docs/projects/img/nand2tetris_2.1/stack_after_add.png
Binary files differ
diff --git a/docs/projects/img/nand2tetris_2.1/stack_after_call.png b/docs/projects/img/nand2tetris_2.1/stack_after_call.png
new file mode 100644
index 0000000..d4a7311
--- /dev/null
+++ b/docs/projects/img/nand2tetris_2.1/stack_after_call.png
Binary files differ
diff --git a/docs/projects/img/nand2tetris_2.1/stack_after_function.png b/docs/projects/img/nand2tetris_2.1/stack_after_function.png
new file mode 100644
index 0000000..b752f93
--- /dev/null
+++ b/docs/projects/img/nand2tetris_2.1/stack_after_function.png
Binary files differ
diff --git a/docs/projects/img/nand2tetris_2.1/stack_after_return.png b/docs/projects/img/nand2tetris_2.1/stack_after_return.png
new file mode 100644
index 0000000..4a8ea50
--- /dev/null
+++ b/docs/projects/img/nand2tetris_2.1/stack_after_return.png
Binary files differ
diff --git a/docs/projects/img/nand2tetris_2.1/stack_before_add.png b/docs/projects/img/nand2tetris_2.1/stack_before_add.png
new file mode 100644
index 0000000..4f13a1b
--- /dev/null
+++ b/docs/projects/img/nand2tetris_2.1/stack_before_add.png
Binary files differ
diff --git a/docs/projects/img/nand2tetris_2.1/stack_before_call.png b/docs/projects/img/nand2tetris_2.1/stack_before_call.png
new file mode 100644
index 0000000..408d626
--- /dev/null
+++ b/docs/projects/img/nand2tetris_2.1/stack_before_call.png
Binary files differ
diff --git a/docs/projects/img/nand2tetris_2.1/stack_before_restore.png b/docs/projects/img/nand2tetris_2.1/stack_before_restore.png
new file mode 100644
index 0000000..b45ccd2
--- /dev/null
+++ b/docs/projects/img/nand2tetris_2.1/stack_before_restore.png
Binary files differ
diff --git a/docs/projects/img/nand2tetris_2.1/stack_before_return.png b/docs/projects/img/nand2tetris_2.1/stack_before_return.png
new file mode 100644
index 0000000..8aabcb2
--- /dev/null
+++ b/docs/projects/img/nand2tetris_2.1/stack_before_return.png
Binary files differ
diff --git a/docs/projects/img/nand2tetris_2.1/stack_call_backup.png b/docs/projects/img/nand2tetris_2.1/stack_call_backup.png
new file mode 100644
index 0000000..b6a9607
--- /dev/null
+++ b/docs/projects/img/nand2tetris_2.1/stack_call_backup.png
Binary files differ
diff --git a/docs/projects/img/nand2tetris_2.1/stack_push.png b/docs/projects/img/nand2tetris_2.1/stack_push.png
new file mode 100644
index 0000000..3395169
--- /dev/null
+++ b/docs/projects/img/nand2tetris_2.1/stack_push.png
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/anime.jpg b/docs/projects/img/reflow-workshop/anime.jpg
new file mode 100644
index 0000000..e135fea
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/anime.jpg
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/avrdude.png b/docs/projects/img/reflow-workshop/avrdude.png
new file mode 100644
index 0000000..d001367
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/avrdude.png
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/bicycle.jpg b/docs/projects/img/reflow-workshop/bicycle.jpg
new file mode 100644
index 0000000..a817e9f
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/bicycle.jpg
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/bolted_stencil.jpg b/docs/projects/img/reflow-workshop/bolted_stencil.jpg
new file mode 100644
index 0000000..4bb0503
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/bolted_stencil.jpg
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/cap_reflow.mp4 b/docs/projects/img/reflow-workshop/cap_reflow.mp4
new file mode 100644
index 0000000..d988fb8
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/cap_reflow.mp4
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/electronic_cat.jpg b/docs/projects/img/reflow-workshop/electronic_cat.jpg
new file mode 100644
index 0000000..dfdfa57
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/electronic_cat.jpg
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/empty_tjy.jpg b/docs/projects/img/reflow-workshop/empty_tjy.jpg
new file mode 100644
index 0000000..41e9928
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/empty_tjy.jpg
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/first_prototype.jpg b/docs/projects/img/reflow-workshop/first_prototype.jpg
new file mode 100644
index 0000000..7aa39f0
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/first_prototype.jpg
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/flawed_qfn.jpg b/docs/projects/img/reflow-workshop/flawed_qfn.jpg
new file mode 100644
index 0000000..9946539
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/flawed_qfn.jpg
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/hotplate.jpg b/docs/projects/img/reflow-workshop/hotplate.jpg
new file mode 100644
index 0000000..801e442
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/hotplate.jpg
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/hotplate_sat.jpg b/docs/projects/img/reflow-workshop/hotplate_sat.jpg
new file mode 100644
index 0000000..b388929
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/hotplate_sat.jpg
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/japanspachtel.jpg b/docs/projects/img/reflow-workshop/japanspachtel.jpg
new file mode 100644
index 0000000..0805f8b
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/japanspachtel.jpg
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/keychain.jpg b/docs/projects/img/reflow-workshop/keychain.jpg
new file mode 100644
index 0000000..0e07396
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/keychain.jpg
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/kliments_vs_mine.jpg b/docs/projects/img/reflow-workshop/kliments_vs_mine.jpg
new file mode 100644
index 0000000..cc988a0
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/kliments_vs_mine.jpg
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/partial_test_run_hotplate.jpg b/docs/projects/img/reflow-workshop/partial_test_run_hotplate.jpg
new file mode 100644
index 0000000..ca17946
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/partial_test_run_hotplate.jpg
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/partial_test_run_joints.jpg b/docs/projects/img/reflow-workshop/partial_test_run_joints.jpg
new file mode 100644
index 0000000..f968e68
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/partial_test_run_joints.jpg
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/partial_test_run_paste.jpg b/docs/projects/img/reflow-workshop/partial_test_run_paste.jpg
new file mode 100644
index 0000000..ad4af4e
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/partial_test_run_paste.jpg
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/paste_best.jpg b/docs/projects/img/reflow-workshop/paste_best.jpg
new file mode 100644
index 0000000..e35f985
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/paste_best.jpg
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/people_who.jpg b/docs/projects/img/reflow-workshop/people_who.jpg
new file mode 100644
index 0000000..ff8645b
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/people_who.jpg
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/perfect_qfn.jpg b/docs/projects/img/reflow-workshop/perfect_qfn.jpg
new file mode 100644
index 0000000..4190cbf
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/perfect_qfn.jpg
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/pick_and_place.jpg b/docs/projects/img/reflow-workshop/pick_and_place.jpg
new file mode 100644
index 0000000..73f9a0e
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/pick_and_place.jpg
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/poster.png b/docs/projects/img/reflow-workshop/poster.png
new file mode 100644
index 0000000..5496b3f
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/poster.png
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/poster_printing.jpg b/docs/projects/img/reflow-workshop/poster_printing.jpg
new file mode 100644
index 0000000..897c732
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/poster_printing.jpg
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/product_sat.jpg b/docs/projects/img/reflow-workshop/product_sat.jpg
new file mode 100644
index 0000000..398cbf6
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/product_sat.jpg
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/qfn32_joint_short.jpg b/docs/projects/img/reflow-workshop/qfn32_joint_short.jpg
new file mode 100644
index 0000000..663ba9a
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/qfn32_joint_short.jpg
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/qfn32_paste_sat.jpg b/docs/projects/img/reflow-workshop/qfn32_paste_sat.jpg
new file mode 100644
index 0000000..df3d818
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/qfn32_paste_sat.jpg
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/qfn32_paste_short.jpg b/docs/projects/img/reflow-workshop/qfn32_paste_short.jpg
new file mode 100644
index 0000000..67f7435
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/qfn32_paste_short.jpg
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/qfn32_paste_success.jpg b/docs/projects/img/reflow-workshop/qfn32_paste_success.jpg
new file mode 100644
index 0000000..976e578
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/qfn32_paste_success.jpg
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/qfn32_paste_sun.jpg b/docs/projects/img/reflow-workshop/qfn32_paste_sun.jpg
new file mode 100644
index 0000000..75fd12f
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/qfn32_paste_sun.jpg
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/qfn32_socket.jpg b/docs/projects/img/reflow-workshop/qfn32_socket.jpg
new file mode 100644
index 0000000..76e69a8
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/qfn32_socket.jpg
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/qfn32_socket_loaded.jpg b/docs/projects/img/reflow-workshop/qfn32_socket_loaded.jpg
new file mode 100644
index 0000000..3a13eb3
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/qfn32_socket_loaded.jpg
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/reflow_workshop_title.png b/docs/projects/img/reflow-workshop/reflow_workshop_title.png
new file mode 100644
index 0000000..664255b
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/reflow_workshop_title.png
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/solder_paste_scraped.jpg b/docs/projects/img/reflow-workshop/solder_paste_scraped.jpg
new file mode 100644
index 0000000..2621dfa
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/solder_paste_scraped.jpg
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/stencils.jpg b/docs/projects/img/reflow-workshop/stencils.jpg
new file mode 100644
index 0000000..e8d93e9
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/stencils.jpg
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/trash.jpg b/docs/projects/img/reflow-workshop/trash.jpg
new file mode 100644
index 0000000..e7379ad
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/trash.jpg
Binary files differ
diff --git a/docs/projects/img/reflow-workshop/usbasp.jpg b/docs/projects/img/reflow-workshop/usbasp.jpg
new file mode 100644
index 0000000..6eadcce
--- /dev/null
+++ b/docs/projects/img/reflow-workshop/usbasp.jpg
Binary files differ
diff --git a/docs/projects/img/sirtet/demo.mp4 b/docs/projects/img/sirtet/demo.mp4
new file mode 100644
index 0000000..90031d7
--- /dev/null
+++ b/docs/projects/img/sirtet/demo.mp4
Binary files differ
diff --git a/docs/projects/img/sirtet/sirtet.png b/docs/projects/img/sirtet/sirtet.png
new file mode 100644
index 0000000..872e062
--- /dev/null
+++ b/docs/projects/img/sirtet/sirtet.png
Binary files differ
diff --git a/docs/projects/index.md b/docs/projects/index.md
index a71cff8..f28afe3 100644
--- a/docs/projects/index.md
+++ b/docs/projects/index.md
@@ -8,7 +8,74 @@ MkDocs). But the few that do, are here.
Projects below are sorted reverse chronologically (most recent first).
-## [One tøp song](one_top_song)
+## [I DONT KNOW HOW BUT I MADE A PCB](idkhow-pcb.md)
+
+![A stack of bare-copper PCBs carrying a logo](img/idkhow-pcb/stack.jpg)
+
+Imma hand them out at the IDKHOW show on April 5th.
+
+## [blobcat PCB](blobcat-pcb.md)
+
+![Yellow PCB standing, colorful LEDs in marquee mode. Printed on PCB:
+"TechJI 2023" and "owo :blobcat:"](img/blobcat-pcb/marquee.gif)
+
+Fun? Yes. Stupid? 100%. It has no value other than blinky blinky.
+
+## [Byseekel](byseekel.md)
+
+![Both sides of a soldered PCB](img/byseekel/rev3.jpg)
+
+Sequel to [Bikeblinkers](#bikeblinkers), and a cautionary tale of why you
+shouldn't overcomplicate your project.
+
+## [Reflow Workshop: A Journal](reflow-workshop.md)
+
+![Two PCBs with a cat printed on the silkscreen](img/reflow-workshop/kliments_vs_mine.jpg)
+
+My first DIY workshop, adapted from [CyberSaturday: SMD for terrified
+beginners](https://tkkrlab.nl/en/cybersaturdays/2021_11_20_smd_for_terrified_beginners__kliment_yanev/).
+
+First I watched the video and taught myself. One month later, I taught two
+friends. Another 2 days hence, we taught 17 people. In total, 20 terrified
+beginners are now ex-terrified beginners.
+
+## [bash workshop](bash-workshop.md)
+
+![Photo of workshop](img/bash-workshop/workshop.jpg)
+
+My first workshop made from scratch. Well prepared tech-wise but not
+social-wise.
+
+## nand2tetris
+
+### [Part 1](nand2tetris_1.md)
+
+![Diagram of a simple computer](img/nand2tetris_1/computer_registers.png)
+
+In July 2022 I enrolled in a course called nand2tetris. In part one of
+this course I built a computer from NAND gates and ran assembly on it. It
+was great fun.
+
+### [Part 2.1](nand2tetris_2.1.md)
+
+![Diagram of a stack](img/nand2tetris_2.1/stack_before_add.png)
+
+A few days after Part 1 was finished, I entered Part 2. There were so many
+things ahead of me that I decided to split it into multiple blogposts. In
+Part 2.1 I learned about the stack machine and wrote a VM translator.
+
+### [Part 2.2](nand2tetris_2.2.md)
+
+In nand2tetris 2.2 I built a tokenizer for a simple language, Jack.
+
+## [SIRTET](sirtet.md)
+
+![Screenshot of SIRTET mid-game](img/sirtet/sirtet.png)
+
+In June 2022 I made a game in C. It was my first time using ncurses. Also,
+I no longer fear pointers (although I'd still keep away from them).
+
+## [One tøp song](one_top_song.md)
![Screenshot of desktop UI](img/one_top_song/ui_desktop.png)
@@ -19,7 +86,7 @@ Here are the steps I took over the course of this project, from
downloading the lyrics, to generating a dataset, and finally making
a game.
-## [Kanvas](kanvas)
+## [Kanvas](kanvas.md)
![Screenshot of Kanvas 0.1.1](img/kanvas/screenshot_0.1.1.png)
@@ -28,7 +95,7 @@ Wallpaper Engine. I feel happy for him, but I'm disappointed that I can't
use it. So I went ahead and wrote my own Plasma widget (or applet, or
plasmoid).
-## [Bikeblinkers](bikeblinkers)
+## [Bikeblinkers](bikeblinkers.md)
[
![
@@ -43,7 +110,16 @@ I made all sorts of mistakes along the way, as I always do, and this
blogpost has all that yelling-at-past-myself covered. It also covers
_some_ actual project details.
-## [Rickstodon](rickstodon)
+## [fkfd.me](fkfdme.md)
+
+![Grid of icons used on homepage](img/fkfdme/icons.png)
+
+You're reading it right now!
+
+The blogpost is on the history and design of my blog — how it came into
+being, what changed, and how it's going.
+
+## [Rickstodon](rickstodon.md)
![Demo](img/rickstodon/rickstodon.webp)
diff --git a/docs/projects/kanvas.md b/docs/projects/kanvas.md
index f0bffa4..6895c6b 100644
--- a/docs/projects/kanvas.md
+++ b/docs/projects/kanvas.md
@@ -1,7 +1,7 @@
# Kanvas
Note: this blogpost may be edited to reflect future changes in the
-project. Current version is 0.1.1, released 2022-05-05.
+project. Current version is 0.1.2, released 2023-07-21.
## Background story
@@ -18,7 +18,7 @@ And two more reasons why I *don't* use it:
- The traffic is unencrypted (He enabled TLS later)
So I went ahead to make one for my desktop environment, KDE Plasma. The
-latest release is 0.1.1.
+latest release is 0.1.2.
![Rectangular widget split vertically into "Announcements" and
"Assignments". An announcement is marked important, i.e. bold red. Several
@@ -30,7 +30,12 @@ Nomenclature: an "activity" is either an announcement or an assignment.
## Download
- [OpenDesktop Pling](https://www.pling.com/p/1783206/)
-- [Direct download](https://fkfd.me/static/kanvas-0.1.1.plasmoid)
+- [Direct download](https://fkfd.me/static/kanvas-0.1.2.plasmoid)
+
+## Source
+
+- [KDE invent](https://invent.kde.org/fakefred/kanvas)
+- [sr.ht](https://git.sr.ht/~fkfd/kanvas)
## Features
@@ -48,6 +53,24 @@ Nomenclature: an "activity" is either an announcement or an assignment.
- Click "Refresh" to, you know, refresh.
- Both lists are automatically refreshed at a configurable interval.
+## v0.1.2 Update and Disclaimer
+
+I used to assume the `has_submitted_submissions` property of an assignment
+in the API response meant _your_ submissions. It turns out that it means
+anyone in your class.
+
+In v0.1.2 I fixed the one-year-old bug. The updated Kanvas makes another
+API request to find your submission. Now, an assignment will show up as
+"submitted" if:
+
+- you actually clicked "submit"
+- you didn't, but the TAs have graded it
+
+The latter may happen to exams on paper.
+
+Still, though, it is *your responsibility* to double check whether an
+assignment is actually submitted or not.
+
## Configuration
To use Kanvas, first you need to generate an OAuth2 token from Canvas
diff --git a/docs/projects/nand2tetris_1.md b/docs/projects/nand2tetris_1.md
new file mode 100644
index 0000000..4766ead
--- /dev/null
+++ b/docs/projects/nand2tetris_1.md
@@ -0,0 +1,945 @@
+# nand2tetris, Part 1
+
+2022-08-18
+
+> _"It takes building one yourself to realize you don't deserve a computer."_
+
+On 2022-07-22, I finally clicked into the bookmark that had been sitting
+in my "University readlist" folder, untouched, since last August. Somehow
+I justed wanted to build a computer all of a sudden.
+
+First a bit of background. [Nand to Tetris](https://www.nand2tetris.org/)
+is a course available online by Noam Nisan and Shimon Shocken that teaches
+you how to build a modern (albeit extremely simple) computer all the way
+up from NAND gates. The course is divided in two parts, to which I have
+given the following nicknames:
+
+- Part 1, NAND to Computer
+- Part 2, Computer to Tetris
+
+The first part of the course consists of 6 projects. On 2022-08-16
+I completed all of them. In this blogpost I will recap what I learned from
+the course, and from my mistakes. __WARNING: There will be spoilers. If
+all you need is reasons why you should enroll, go to [Course
+review](#course-review)__.
+
+I've uploaded my current projects to [this git repo](https://git.fkfd.me/nand2tetris.git/).
+
+## Project 01: Elementary gates
+
+In this project I built a few basic logic gates, such as AND, XOR, and the
+(De)Mux, using a custom HDL language invented for the course. Here is the
+code for the AND gate:
+
+```
+CHIP And {
+ IN a, b;
+ OUT out;
+
+ PARTS:
+ Nand(a=a, b=b, out=nout);
+ Not(in=nout, out=out);
+}
+```
+
+Graphically, the HDL describes the following schematic:
+
+![A NAND connected to a NOT](img/nand2tetris_1/and_gate_nand_not.png)
+
+Here `a`, `b` and `out` are hardcoded, but `nout` is an arbitrary name
+I gave to the internal pin. If we further substitute the implementation of
+`Not` in `Nand`, this is what _actually_ lives on the silicon:
+
+![Two NANDs](img/nand2tetris_1/and_gate_nand.png)
+
+Once we have written this HDL code, we can encapsulate the AND gate in the
+following symbol, which is in essence two NAND gates in a trenchcoat:
+
+![One AND](img/nand2tetris_1/and_gate.png)
+
+And this is one of the few chips that have so few pins exposed (hence
+"elementary") that you can craft a truth table for it, and thus can test
+them manually.
+
+<details markdown="1">
+<summary markdown="1">
+ __How I built and optimized the XOR gate by toying with boolean
+ expressions (click to expand)__
+</summary>
+
+Here is the truth table of XOR, and you have to implement it with chips
+you already made, which are AND, OR, NOT, and NAND.
+
+```
+ a | b | out
+---|---|----
+ 0 | 0 | 0
+ 0 | 1 | 1
+ 1 | 0 | 1
+ 1 | 1 | 0
+```
+
+The solution is not immediately obvious (you can skip this part if you
+find it so). It is proven that, given any truth table of finite columns,
+you can construct a boolean expression satisfying it using what's called
+a "disjunctive normal form". Here's how it works. First you extract every
+row that outputs 1:
+
+```
+ a | b | out
+---|---|----
+ 0 | 1 | 1
+ 1 | 0 | 1
+```
+
+Then we tie a NOT to each 0 input (excuse the pun), thus synthesizing two
+sufficient conditions so that `out=1`:
+
+```
+(NOT a) AND b => out=1
+a AND (NOT b) => out=1
+```
+
+Finally, we OR them both, resulting in a single boolean expression:
+
+```
+((NOT a) AND b) OR (a AND (NOT b))
+```
+
+From a pragmatic perspective, our job is done. But the whole premise of
+nand2tetris is that, we're building every chip out of NAND gates, and I'm
+challenging myself to use as few as possible. So let's count how many NAND
+gates we need to make a XOR.
+
+```
+Gate | # of NAND gates
+-----|----------------
+NAND | 1
+NOT | 1
+AND | 2
+OR | 3
+XOR | ?
+```
+
+```
+((NOT a) AND b) OR (a AND (NOT b))
+ ^ ^ ^ ^ ^
+ 1 2 3 2 1
+```
+
+That's 9 in total. Can we optimize it? Let's try eliminating the OR first,
+based on the fact that `a OR b = NOT((NOT a) AND (NOT b))`:
+
+```
+ ((NOT a) AND b) OR (a AND (NOT b))
+= NOT((NOT((NOT a) AND b)) AND (NOT(a AND (NOTb))))
+```
+
+At first glance this might seem more complicated, but remember,
+`NOT(a AND b) = a NAND b`… which is exactly our atomic building
+block! We continue our equation:
+
+```
+= ((NOT a) NAND b) NAND (a NAND (NOT b))
+```
+
+Since NOT and NAND are both made of one single gate, our optimized number
+of gates is 5. (However, this optimization did not matter for a reason
+I will explain later.)
+</details>
+
+There were many other chips I was already familiar with, so I implemented
+them with little trouble apart from the HDL syntax.
+
+## Project 02: ALU
+
+The goal of building a computer in this theoretical approach is to conjure
+up a way to make a pool of NAND gates capable of thinking. This project
+does exactly that. Yes, you are right: I built an ALU.
+
+The fundamental idea of an ALU is that, you give it two inputs `x` and `y`
+and pick a function `h`, and for some reason it outputs `h(x, y)` where
+`h` is one of the following:
+
+```
+0, 1, -1, // constant value
+x+y, x-y, y-x // arithmetics
+x, y, -x, -y, !x, !y, // single variable function
+x+1, y+1, x-1, y-1, // one variable + constant
+x&y, x|y // boolean operation
+```
+
+You supply 6 bits known as "control bits" that let the ALU decide what
+function you want. They are:
+
+- `zx`, `zy`: coerce x or y to zero when set
+- `nx`, `ny`: negate x or y when set (binary NOT)
+- `f`: performs binary arithmetics when set, boolean operation otherwise
+- `no`: negate output-to-be
+
+But how do you know when you need to negate or zero the operands? Doesn't
+this call for an `if` statement? Well, in project 01 I made a chip named
+`Mux16` and it is _tremendously_ helpful. It's the hardware embodiment of
+`if () ... else ...`.
+
+![Schematic of a mux](img/nand2tetris_1/mux.png)
+
+(In reality we are using the 16-bit version, but in principle they're the
+same.)
+
+This means I just need to hook `nx` and `zx` to `sel` pins, and route
+`a`'s and `b`'s somewhere.
+
+The HDL that does this job for `x` is:
+
+```
+Mux16(a=x, b=false, sel=zx, out=zdx); // false = all 0's
+Not16(in=zdx, out=ndx);
+Mux16(a=zdx, b=ndx, sel=nx, out=px);
+```
+
+Graphically:
+
+![Two muxes in series outputting "px"](img/nand2tetris_1/zx_nx.png)
+
+(The order of the muxes matters, because when `zx = nx = 1`, this logic
+configuration yields `~0 = -1` while the other way gives you 0.)
+
+The same goes for `y`. Now that we have the processed version of `x` and
+`y`, denoted `px` and `py`, it's time to force this pile of imaginary NAND
+gates to do math for us. The boolean AND operation is trivial, so we will
+only be talking about the Adder, which is a chip that takes two binary
+numbers `x` and `y` and outputs `(x+y) % 65536`, that is, carries every
+bit except the MSB.
+
+But wait! Didn't we just see "x-y" and "y-x"? How can an Adder possibly do
+that? Note that there is no "Subtractor"; to do `x-y` you simply calculate
+`~(~x + y)` where `~` denotes bitwise negation, thanks to 2's complement.
+
+<details markdown="1">
+<summary markdown="1">__Proof that `x-y = ~(~x + y)`__</summary>
+
+We know in 2's complement,
+
+```
+~x + x = 0xffff = -1 <==> ~x = -1 - x
+```
+
+Add y to both sides, we get
+
+```
+~x + y = -1 - x + y
+```
+
+Therefore, by means of negation,
+
+```
+~(~x + y) = -1 - (~x + y)
+ = -1 - (-1 - x + y)
+ = -1 + 1 + x - y
+ = x - y ⌷
+```
+</details>
+
+(Nisan and Shocken have supplied this course with a table where you can
+look up the ALU output given six control bits, or vice versa, but it won't
+be a necessity until project 06.)
+
+In addition to a 16-bit output bus (we call a group of indexable pins
+a "bus"), the ALU outputs two flags: `ng` and `zr`. If the final output is
+negative, `ng` is set; if it is zero, `zr` is set. The clever thing is,
+`ng` is just the highest bit of the output, i.e. `out[15]`, and `zr` is
+set iff every bit in `out` is zero.
+
+That's it, we have completed the ALU! I know y'all are anticipating the
+schematics; how can I let you down?
+
+![Schematic of ALU](img/nand2tetris_1/alu.png)
+
+For some reason nand2tetris only provided me with `Or8Way`, which OR's
+together 8 bits, so I had to use two of them to do all 16. I tried writing
+an `Or16Way` "helper chip" in the project 01 folder, but when I tried to
+include it in the ALU, it did not work. Why?
+
+When you tell the hardware simulator to load a chip, it first looks for
+`<chip_name>.hdl` under the working directory; if there's no such file, it
+proceeds to look for builtin chips implemented in Java. No other directory
+is in its path, even project 01. This explains why the optimization I made
+in project 01 does not matter.
+
+<details markdown="1">
+<summary markdown="1">__If it did matter, how many NANDs are there in this ALU?__</summary>
+
+It's Python scripting time! I just need to recursively find out how many
+NANDs in each subchip, then add them up. The syntax of HDL is pretty
+simple, and the script goes as follows (simplified):
+
+```
+HDL_PROJECTS = ["01", "02", "03/a", "03/b", "05"]
+
+CHIPS = {
+ "Nand": 1,
+ "DFF": 6, # https://commons.wikimedia.org/wiki/File:Edge_triggered_D_flip_flop.svg
+}
+
+
+def count_nands(project_path, chip):
+ # figure out where the HDL is
+ f = None
+ for dir in HDL_PROJECTS:
+ try:
+ f = open(project_path + dir + f"/{chip}.hdl")
+ break
+ except FileNotFoundError:
+ continue
+
+ if f is None:
+ print(f"Chip not found: {chip}", file=stderr)
+ return 0
+
+ while f.readline().strip() != "PARTS:":
+ continue
+
+ nands = 0
+ line = f.readline() # e.g. " Not (in=in, out=out); // comment"
+ while line:
+ code = line.split("//")[0].strip() # e.g. "Not (in=in, out=out);"
+ if "(" not in code:
+ line = f.readline()
+ continue
+ subchip = code.split("(")[0].strip() # e.g. "Not"
+ if subchip not in CHIPS:
+ CHIPS[subchip] = count_nands(project_path, subchip)
+ print(f"{subchip}\t{CHIPS[subchip]}")
+
+ nands += CHIPS[subchip]
+ line = f.readline()
+
+ f.close()
+ return nands
+
+chip = "ALU"
+nands = count_nands("/path/to/nand2tetris/projects/", chip)
+print(f"There are {nands} NAND gates in {chip}")
+```
+
+The output is (vertically aligned):
+
+```
+Not 1
+And 2
+Or 3
+Mux 8
+Mux16 128
+Not16 16
+And16 32
+Xor 5
+HalfAdder 7
+FullAdder 17
+Add16 262
+Or8Way 21
+There are 1156 NAND gates in ALU
+```
+
+Pretty sure my algorithm's right. Honestly it's fewer gates than
+I imagined because the ALU seemed to do so many things.
+</details>
+
+Most of the chips I made in project 02 are too complex to test by hand,
+but fortunately nand2tetris provides test scripts and compare files you
+can run in a hardware simulator that simulates the logic described by the
+HDL.
+
+While I was drawing the schematics, a question came to mind: If I set `f`
+to zero, will the Adder be working the same way as if `f=1`?
+
+<details markdown="1">
+<summary markdown="1">__Answer__</summary>
+
+Yes, it will. Simply put, chips "downstream" do not directly affect those
+"upstream" as far as this course is concerned. The implication is that,
+when you give an x and a y to the ALU, it does both computations at once:
+boolean AND and binary addition. It is the Mux's job to select which
+branch to pass downstream, and which one to discard. This is, in
+a stretch, called [speculative execution](https://en.wikipedia.org/wiki/Speculative_execution)
+
+![Schematic of ALU. The AND, Adder, and "f" mux are highlighted](img/nand2tetris_1/alu_highlighted.png)
+
+As you see in the highlighted area, _both_ of these gates are
+switching internally, and both of them consume power, even when one of
+them does not generate useful output. Recall the `if` statement
+in programming:
+
+```
+if (boolean) {
+ do_a();
+} else {
+ do_b();
+}
+```
+
+Either but not both of `do_a` or `do_b` will be run.
+This is one of the major mindblowers I came into, and I think it's worth
+writing about it.
+</details>
+
+## Project 03: Memory
+
+In project 02 we made an ALU think, but only once. This is because it
+doesn't remember anything. So if we want to build a computer, we need to
+make something that keep a record of what has been done, and what is left
+to do.
+
+The D flip-flop (DFF) is a chip that remembers one bit of information for
+one clock cycle. It's the fundamental building block for sequential logic
+in nand2tetris. Limitations in the simulator forbid building it from NAND
+gates yourself, because to do that you would need to wire an output into
+the input, which forms an infinite loop which the simulator is incapable
+of resolving. Instead, it provides a builtin chip, `DFF`, using which you
+can build registers from 1 bit to 16K words.
+
+Here's how a register works from the user's point of view:
+
+![Schematic of chip with in, load, and out pins](img/nand2tetris_1/register.png)
+
+If you give it some data via `in` and pull `load` to logic one, the data
+will show up in `out` at the next clock cycle. However as long as `load`
+remains zero, `out` does not change, as if "remembering" the data.
+
+But doesn't the D flip-flop only keep its data for one clock cycle? Yes,
+but we can find our way around it. We simply use a Mux as a "valve": it
+only lets `in` pass through when `load` is high. Otherwise, we feed `out`
+into the DFF again, creating an eternal feedback loop, but this time the
+simulator handles it with no problem, because the loop is delayed by one
+clock cycle each step.
+
+![Schematic of Mux and DFF in register](img/nand2tetris_1/register_internal.png)
+
+The RAM I proceeded to build is just an array of arrays of arrays of … of
+arrays of registers. Just copy and paste; no thinking required.
+
+The Program Counter (PC for short) however, ground my brains like jelly in
+a blender. Its job is to remember a number, increment it by one each clock
+cycle, and set it to some other value when required. The user controls it
+using three control pins: `load`, `inc`, and `reset`. The increment
+happens when `inc=1`, unless `load=1`, in which case the PC swallows the
+data on the `in` bus and spits it out at the next clock cycle, then
+increment that value from now on. `reset`, of course, forces the output to
+zero.
+
+It's not hard to think of using the Register for storage and Inc16 (16-bit
+incrementer) to do the math. The rest is Muxes, just like this:
+
+![PC with "out" wired to output of the final "reset" mux](img/nand2tetris_1/pc_incorrect.png)
+
+There is a problem with this solution. Do you see it? Let's read the chip
+specs again carefully:
+
+```
+if (reset[t] == 1) out[t+1] = 0
+else if (load[t] == 1) out[t+1] = in[t]
+else if (inc[t] == 1) out[t+1] = out[t] + 1 (integer addition)
+else out[t+1] = out[t]
+```
+
+Note that the first line tells us that `out` should be zero if `reset` at
+the _previous_ clock cycle is one, whereas in the schematic above, `out`
+becomes zero as soon as `reset` rises to one, because the Mux is
+a combinatorial chip — it does not wait for the clock. So what can we do?
+We just need a way to delay `out` by one clock cycle. The only sequential
+chip here is the Register, so let's try hooking `out` onto its output
+instead:
+
+![PC with "out" wired to output of Register](img/nand2tetris_1/pc.png)
+
+Simulation tells me that this works perfectly, but deep inside I feel I'm
+still extremely sloppy at clocked logic.
+
+## Project 04: Assembly
+
+There's no HDL in project 04. Instead, I learned to write assembly. Nisan
+and Shocken, once again, invented an assembly language for this very
+architecture called HACK. The language understands two sorts of
+instructions. But first, we need to imagine we have a computer.
+
+![Incomplete schematic of computer. Inside blue rectangle: ROM, CPU, RAM.
+Inside the CPU is a blob labeled "ALU and stuff", A Register, and D Register.
+Outside: Reset button, screen, keyboard.](img/nand2tetris_1/computer_registers.png)
+
+Here you see three devices inside the blue rectangle: ROM, CPU, and RAM.
+
+- ROM: Read-Only Memory. Our computer fetches and executes instructions
+ from it. We did not write HDL for it because it's just a read-only
+ version of the RAM, and it's builtin.
+- CPU: An encapsulation of the ALU that works by black magic until project
+ 05.
+- RAM: Random Access Memory. Our computer can read and write data in it.
+
+HACK assembly provides an extremely rudimentary way to control the CPU.
+The key to understanding HACK assembly is the three registers: D, A, and
+M. D stands for "Data" (probably). It holds a 16-bit value; so do the
+other two. You can do two things to it in our assembly language:
+
+- Read it as an ALU input
+- Write ALU result into it
+
+A stands for "Address". It works pretty much the same way D does, with
+one more added function:
+
+- Set it to an assemble-time constant
+
+which can be realized using this line of assembly (or instruction):
+
+```
+@42
+```
+
+<details markdown="1">
+<summary markdown="1">__Expand to learn more about the difference between A and D__</summary>
+
+In HACK assembly, the A register is the only one you can directly assign
+an value to, like we just did. In contrast, if you want to write 42 into
+D, you will need to go through two steps:
+
+```
+@42 // this sets A to 42
+D=A // this copies the value of A, 42, into D
+```
+
+However, if you need to set A to some non-constant, you have to use
+another trick:
+
+```
+A=D // this sets A to current value of D
+```
+</details>
+
+M stands for "Memory". Note that it is not a register in the literal
+sense; it is actually an alias for RAM[A]. For example,
+
+```
+@42
+M=-1
+```
+
+sets the 42nd word (if you count from zero) in RAM to -1, i.e. set all
+bits to one.
+
+We call an instruction an "A-instruction" if it begins with "@" and
+"C-instruction" otherwise. By executing a C-instruction the ALU always
+computes something — even zero. You can prefix it with a __destination__
+to decide where the result should be saved. You can either pick or not
+pick any of the three registers, A, D, and M, resulting in
+8 possibilities, like this:
+
+```
+D+M; // compute D+M, but do not save it anywhere
+AD=1; // set A and D to 1 simultaneously
+AMD=1 // set A, M and D to 1 simultaneously
+```
+
+C-instructions can also do something really cool, which is __jumping__.
+The syntax is different from what you might find in an industrial assembly
+language.
+
+```
+@42
+D;JEQ // this jumps to ROM[42] if D=0
+```
+
+The `JEQ` here is a __jump instruction__. A jump instruction
+compares ALU output to zero, and jumps if it is greater/equal/not
+equal/etc.
+
+<details markdown="1">
+<summary markdown="1">__What if I want to jump to 42 if RAM[69] is zero?__</summary>
+
+Can I just:
+
+```
+@69
+@42
+M;JEQ // does this work?
+```
+
+No. `@42` completely overwrote `@69`, so the M on
+line 3 is actually RAM[42]. To perform this maneuver, we need to make use
+of D.
+
+```
+@69 // set A to 69
+D=M // D is now RAM[69]
+@42 // set A to 42
+D;JEQ // if D=0, jump to ROM[42]
+```
+</details>
+
+For convenience, we can attach a label to an instruction in assembly and
+reuse it later:
+
+```
+(END)
+@END
+0;JMP
+```
+
+In this code `(END)` points to `@END`, and `JMP` is an unconditional jump,
+so this creates an infinite loop. It is good practice to terminate
+a program with this, lest the ROM address overflows and the computer runs
+the program again from instruction 0.
+
+You can also use custom symbols after an "@" to assign or reuse
+a static address starting from 16. It is like declaring a variable whose
+value is stored in RAM[n] where n ≥ 16. Why not 0-15, you ask? Well, they
+are reserved for symbols R0-R15.
+
+This `@symbol` notation goes further by mapping a monochrome screen to
+a memory area `@SCREEN`, namely 16384-24575, and a keyboard to `@KBD`,
+which is 24576. If you write the highest bit to one in RAM[16384], you
+paint the top left pixel black. If you press space, it sets RAM[24576] to
+32 (0x20). You can interact with them in the CPU emulator.
+
+!["Pong" running in the CPU emulator](img/nand2tetris_1/cpu_emulator_pong.png)
+
+▲ CPU Emulator running Pong
+
+Here's a snippet of HACK assembly I wrote that increments RAM[16] from
+0 until it is equal to whatever you put in RAM[0] before running the
+program:
+
+```
+@x
+M=0 // x = RAM[16] = 0
+
+(LOOP)
+@R0
+D=M // D = RAM[0]
+@x
+D=D-M // D -= x
+@END
+D;JEQ // if D == 0 goto END
+@x
+M=M+1 // otherwise x += 1
+@LOOP
+0;JMP // goto LOOP
+
+(END)
+@END
+0;JMP
+```
+
+## Project 05: Computer
+
+After a project that seemed to come out of nowhere, it sounds reassuring
+that we're back to building the computer hardware. First we need to decide
+the architecture. It is common to build a computer on the Von Neumann
+architecture. But canon Von Neumann stores both instructions and data in
+a single memory unit, commonly RAM, so the CPU can modify both. But even
+so, a tiny ROM is needed to instruct the CPU in its booting process. In
+our application however, we will just straightforward use two units simiar
+in size. This means given a single ROM, our computer can only run one
+specific program. This is called the Harvard architecture, technically
+a subset of Von Neumann. AVR microcontrollers also use this architecture.
+
+So this means there will be three things inside the computer: CPU, ROM,
+and RAM. Since we made RAM in project 03 and ROM is builtin, all that's
+left is the CPU.
+
+The CPU needs to:
+
+- Read instruction from ROM
+- Read data from RAM
+- Compute something
+- Write data to A, D, and RAM
+- Run instruction one by one
+- Jump to another instruction if programmer asks it to
+
+The instant I read the requirements, I knew there will be a ton of
+internal wires. Fortunately, the authors provided a block diagram similar
+to this one:
+
+![Incomplete diagram of CPU. Many labels are question marks](img/nand2tetris_1/cpu_book.png)
+
+Yikes! What's with the question marks? Apparently it's the authors' own
+version of spoiler alerts. I had to figure out what they are by myself,
+and that's a good thing. I like challenges.
+
+Let's first make sure we know what each pin/chip is doing.
+
+- Instruction in ROM comes from pin `instruction`
+- Data from RAM come from pin `inM`
+- Both RAM and ROM addresses are selected using `addressM`
+- The ALU takes two registers and churns out an output
+- The A and D Register accept inputs from ALU
+- ALU output also goes to RAM via `outM`
+- `writeM` tells RAM to load data
+- The PC increments, resets or jumps to instruction
+
+What's an instruction anyway? It's a 16-bit value that describes what we
+want the CPU to do in this clock cycle. An assembler, which we will write
+in project 06, translates assembly to binary code, but in this project
+let's assume it came from nowhere.
+
+What the 16 bits represent depends on the type of instruction you're
+writing. It is specified by the highest bit, called the __opcode__. In an
+A-instruction, the opcode is 0, which is followed by 15 bits of address.
+Considering the size of our larger memory unit, ROM, is 32768 words, 15
+makes sense. In this case the CPU will store the value in the A register.
+
+The C-instruction with opcode 1 is more complicated, but boils down to
+four groups of control bits:
+
+```
+ fixed ALU control jump instruction
+\ / \ / \ /
++-----+---+--------+--------+--------+
+| 111 | a | c1..c6 | d1..d3 | j1..j3 |
++-----+---+--------+--------+--------+
+ / \ / \
+ A/M destination
+```
+
+- `a` decides whether the ALU takes in D and A, or D and M.
+- `c1..c6` correspond to control bits on the ALU.
+- `d1..d3` tell CPU to save ALU output to A, D, and M respectively.
+- `j1..j3` tell CPU to jump to ROM[A] when ALU output <0, =0, and >0,
+ respectively.
+
+In a hunch, it seems we have the answer to most of the question marks.
+
+![CPU but some question marks are replaced with bit names](img/nand2tetris_1/cpu_hunch.png)
+
+This is simple but wrong. Let's take a close look at the `load` pin
+labeled `d1` on the A register. If we try to load address 1 into it, using
+an A-instruction `@1`, what will happen? Let's assemble it:
+
+```
+0000 0000 0000 0001
+^\________________/
+| ^
+| |
+opcode integer value 1
+```
+
+The mux on the left will let the instruction through because `opcode` is
+0, but remember, HDL doesn't recognize `d1`, it only recognizes
+`instruction[5]`. What's gonna happen is, the A register will refuse to
+load, because `instruction[5]` is zero. Something similar will also happen
+to `d2` and `d3`, so we add a little logic:
+
+![CPU with correct logic controls at d1, d2, and d3](img/nand2tetris_1/cpu_controls.png)
+
+This ensures that A, D and M load data if and only if we explicitly tell
+them to. Now we shift our attention to the only two question marks left:
+`zr` and `ng` coming from the ALU, and `load` going into the PC.
+
+Notice something? `j1..j3` are missing, so it's definitely them. Recall
+that we can set the PC to its input if we pull `load` to high, and this
+way we can jump to ROM[A]. But how?
+
+![CPU with a blob labeled "mysterious logic that decides whether we jump
+or not"](img/nand2tetris_1/cpu_mysterious_jump_logic.png)
+
+It's actually very straightforward! Apparently the authors thought it
+through when specifying the ALU.
+
+![Complete diagram of CPU](img/nand2tetris_1/cpu.png)
+
+(Actual gates may differ)
+
+And that's it, we made a CPU! We are super close to the computer; all we
+need to do is connect all the wires. I'm feeling too tired to sketch up
+another diagram so here's the HDL:
+
+```
+CHIP Computer {
+ IN reset;
+
+ PARTS:
+ ROM32K(address=pc, out=instruction);
+ CPU(
+ inM=inM, instruction=instruction, reset=reset,
+ writeM=writeM, outM=outM, addressM=addressM, pc=pc
+ );
+ Memory(in=outM, address=addressM, load=writeM, out=inM);
+}
+```
+
+## Project 06: Assembler
+
+Having gone through project 04 and 05, if you think something's missing,
+then you're right. We haven't talked about how we translate assembly to
+binary instructions. In nand2tetris, you are asked to write an assembler
+in any high-level language (or not write one at all and assemble by hand).
+As the grammar is pretty simple and well-defined, it wouldn't take too
+much effort, yes?
+
+For some reason my self-obsessive ass, after two successful projects in
+C (one of them being [SIRTET](sirtet.md)), decided to write the assembler
+in this very cursed language.
+
+Roast me all you want, but most of the time I'm thinking about stuff like
+"what if the programmer slid in illegal characters in the variable name"
+and "hmm so I malloc a piece of memory and strcpy the second to
+second-to-last characters from this string. We'll need a null terminator
+so the size has to be one byte smaller than the source string blah blah
+blah" and I'll take another minute to decide how long this piece of memory
+will live until I free it.
+
+I shall skip the part where I wrote the program because that will bore you
+and me to death. Instead, let's watch the results if we assemble the
+assembly I showed you in [project 04](#project-04-assembly). The
+executable is called `hack-as`, just like `avr-as`.
+
+```
+$ ./hack-as -v test/inc.asm
+====== SYMBOLS =====
+label addr
+LOOP 2
+END 12
+x 16
+
+====== RESULTS =====
+addr binary inst
+0 0000000000010000 @16
+1 1110101010001000 M=0
+2 0000000000000000 @0
+3 1111110000010000 D=M
+4 0000000000010000 @16
+5 1111010011010000 D=D-M
+6 0000000000001100 @12
+7 1110001100000010 D;JEQ
+8 0000000000010000 @16
+9 1111110111001000 M=M+1
+10 0000000000000010 @2
+11 1110101010000111 0;JMP
+12 0000000000001100 @12
+13 1110101010000111 0;JMP
+
+Binary written to test/inc.hack
+
+$ cat test/inc.hack
+0000000000010000
+1110101010001000
+0000000000000000
+1111110000010000
+0000000000010000
+1111010011010000
+0000000000001100
+1110001100000010
+0000000000010000
+1111110111001000
+0000000000000010
+1110101010000111
+0000000000001100
+1110101010000111
+```
+
+nand2tetris provided me with a few other assembly files I can test my
+assembler against. The longest one was over 28k instructions. All of them
+passed.
+
+As always, I did some memory profiling with valgrind:
+
+```
+$ valgrind --leak-check=full --show-leak-kinds=all ./hack-as test/inc.asm
+==31030== Memcheck, a memory error detector
+...
+Binary written to test/inc.hack
+==31030==
+==31030== HEAP SUMMARY:
+==31030== in use at exit: 0 bytes in 0 blocks
+==31030== total heap usage: 49 allocs, 49 frees, 77,587 bytes allocated
+==31030==
+==31030== All heap blocks were freed -- no leaks are possible
+==31030==
+==31030== For lists of detected and suppressed errors, rerun with: -s
+==31030== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
+```
+
+(It is indeed weird that it needs 77kB of heap memory, but since we've
+been overdoing it all along we might as well just ignore it.)
+
+The whole process took one night, one day, and one morning. And the
+afternoon following that morning, I decided to implement it again using
+Python to see how much easier it is.
+
+The advantage of Python over C is human-oriented string methods like
+`str.split` and `str.partition`. Also, no manual memory management.
+I finished it in less than an hour, not because Python is easy, but
+because I already have C code to refer to. This, in hindsight, is a big
+mistake. I should have prototyped it in Python first, _then_ proceed to
+write it in C. However the speed benefit C provides is an overkill for
+this kind of simple task, so in that case I might just not write C at all.
+
+And thus we have bridged the gap between assembly language and machine
+code. Our computer can run _anything_. (As long as it fits in the ROM)
+
+## Course review
+
+### Conceptual
+
+nand2tetris part 1 takes you on a journey from NAND gates as promised. You
+climb a ladder from the humble logic gate to more and more complex chips,
+representing increasingly advanced logic, but they're NANDs all the way
+down. The authors say that the goal of nand2tetris is to "demystify
+computers": Every time you witness the very chip you made do something
+exciting, like adding up two numbers or remembering 16 bits of data, one
+layer of mystery dissolves, because you _know_ X happens when chip Y does
+Z, not because TSMC soaked your wafer in .25 mol/L holy water.
+Philosophical takeaway: One complicated thing is just many simple things
+arranged in a clever way.
+
+### Technical
+
+The project-oriented approach really gets your hands dirty, but not
+completely covered in copper particles thanks to its simulators. They are
+offered in a software suite, which verifies your work against test scripts
+bundled along. This is the best thing of this course. If you grab some
+university textbook, the first chapter might be like "OK now go download
+NI® Multisim™ for 628 dollars" or "we'll give you an answer to every
+question and you just need to convince yourself it works", whereas the
+nand2tetris software suite is GPL'd. I only have two points of criticism:
+
+- It is buggy at times (the safe kind, you just need to restart it)
+- It is written in Java (I have a prejudice against everything Java)
+
+As I mentioned in [Project 02](#project-02-alu), every time you advance into
+a new project, all the chips you've made thus far become builtin Java code
+instead of HDL. For example, when you simulate the ALU, the
+Mux/And16/whatever you're seeing is no longer what you wrote in project
+01, but part of the simulator itself.
+
+Pro: Complex chips run more efficiently and reliably.
+
+Con: You can't verify your prior chips _actually_ works beyond the test
+scripts.
+
+Of course, the pro beats the con by an order of magnitude. Don't you have
+this deep-rooted fear that one loose jumper wire on the breadboard can
+knock your Ben Eater-style computer completely off the rail?
+
+![DIP chips and LCD connected with numerous jumper wires on three
+breadboards; the LCD reads "6502 Computer kit"](img/nand2tetris_1/6502.jpg)
+
+▲ Image credit: [Ben Eater, "Build a 6502 computer"](https://eater.net/6502)
+
+### Course outcome
+
+The purpose of this course is, in my opinion, to prove to you that
+computers are not magic, and that it is _possible_ to build your own
+computer. It's like one of those workshops that lets you make a little
+bottle opener, but you don't wind up being a blacksmith. That said, the
+course does not bother you with the intricacies and technicalities of
+real-life engineering. Every step is like playing with Legos, except
+there's no microplastic.
+
+Moreover, there is a very valuable "Perspective" unit at the end of each
+project where the instructors, Nisan and Shocken, sit together and explain
+something that's technically outside the scope of this course, but good to
+know nonetheless. For example, how NAND gates are made in CMOS logic and
+how D flip-flops are made from NANDs. This opens up a whole new door if
+you happen to be interested.
+
+To make my own computer almost from scratch is a greatly pleasant
+experience. I would definitely recommend this course.
diff --git a/docs/projects/nand2tetris_2.1.md b/docs/projects/nand2tetris_2.1.md
new file mode 100644
index 0000000..0728f94
--- /dev/null
+++ b/docs/projects/nand2tetris_2.1.md
@@ -0,0 +1,554 @@
+# nand2tetris, Part 2.1
+
+2022-08-25
+
+Last week I completed nand2tetris part 1 and wrote [a blogpost about
+it](nand2tetris_1.md). It was great fun; I like how I built a computer
+from NAND gates. Without missing a beat I went on to work on the sequel:
+nand2tetris, Part 2.
+
+So we have a computer. Now what? Hang it on the wall for the lolz? Of
+course not — let's force the emulation of silicon to think.
+
+Again, this course expects you to finish 6 projects (actually 7 but
+"module 0" is actually a replica of project 04 from part 1). As of the
+time of writing, I've finished two of them. Over time I discovered that
+these two projects alone are already complicated enough. If I were to wait
+until I finish all six, I won't have the motivation to cover them in one
+single monolithic blogpost. I find it more sensible to mark a checkpoint,
+write a blogpost, then carry on doing project 09.
+
+__SPOILER WARNING: this blogpost contains my solutions to many challenges.
+If you haven't taken the course but wish to, please do it before reading
+this.__
+
+## Stack machine
+
+Let's pull out an inventory of things we made in project 06:
+
+- A computer built on the HACK architecture
+- HACK assembly language
+- An assembler from HACK assembly to machine code
+
+What do we do now? Write Tetris in assembly? Of course not. We need
+a high-level language and a compiler. However, the authors find the curve
+too steep from assembly to a high-level language. Therefore, we are
+introduced an abstraction: the __stack machine__, also called virtual
+machine (VM). Our ultimate task in project 07 and 08 is to write a VM
+translator that translates VM commands into one single assembly file,
+where we emulate the stack machine on HACK hardware.
+
+What's a stack? Sure, it's a kind of data structure, but right now we are
+talking about a bunch of contiguous words in RAM and a pointer to the top.
+The stack starts at RAM[256] and goes on until 2047. RAM[0], which in our
+assembler is also called SP, contains the pointer to the top of our stack
+(although technically, it is the address of the topmost word plus one;
+more on that later). Initially, since the stack is empty, SP = 256.
+
+Of course, our machine is much more than that, even in our first two
+projects. Our focus in this blogpost is to understand how the stack
+machine works, how it helps our computer be a computer, and how we
+implement it using HACK assembly.
+
+Our complete RAM is charted as follows. It'll prove useful sooner or
+later.
+
+![](img/nand2tetris_2.1/ram_areas.png)
+
+Nomenclature: Except in assembly code, the symbols SP/LCL/etc. refer to
+the value stored in RAM[0]/RAM[1]/etc., and an asterisk followed by
+a symbol (with or without an offset) dereferences it in C-style, e.g.
+\*(LCL+1) refers to RAM[LCL+1], i.e. RAM[RAM[1] + 1].
+
+## Memory segments
+
+You can do many things to a stack, but the most elementary operations are
+push and pop. Let's say you want to push a number, say 420, onto the
+stack. All you need to do is:
+
+- read SP
+- go to RAM[SP]
+- write 420
+- increment SP by one
+
+The VM command for this is `push constant 420`. You can repeat this as
+many times as you please, and SP will keep updated.
+
+![SP=263, stack: 420 (7 times)](img/nand2tetris_2.1/stack_push.png)
+
+What if we need to push some non-constant number, like RAM[420]? In this
+case, the VM implementation imposes some restrictions. You cannot just ask
+for any memory in RAM; you have to specify one of eight "memory segments".
+Four of them have something to do with the four companions of SP: LCL,
+ARG, THIS, and THAT. In project 06, their purposes were not clearly
+stated, but they're actually pointers to useful chunks of memory at
+a certain point in the program. To push them onto the stack, you follow
+these pointers.
+
+Suppose LCL (RAM[1]) is 420, and I run this VM command: `push local 3`.
+The result is:
+
+- SP is incremented by one
+- \*(SP-1) is equal to RAM[420+3]
+
+LCL is not an assemble-time constant, so the 420+3 is computed in
+runtime like this:
+
+```
+@LCL
+D=M // D is 420
+@3
+A=D+A // A is 423
+D=M // now D is RAM[423]
+```
+
+The other memory segments are dead simple so I will not elaborate.
+
+pop is the exact opposite of push and share a similar syntax. It's worth
+mentioning that:
+
+- You decrement SP _before_ copying top of stack to memory segment
+- There's no need to erase what you just popped from the stack
+- You can't pop to the constant segment
+
+<details markdown="1">
+<summary markdown="1">__Watch me struggle to implement `pop local 3`__</summary>
+
+All I have to do is copy \*(--SP) into \*(LCL+3) in assembly. Easy, right?
+Here goes:
+
+```
+// find out what's on top of stack
+@SP
+AM=M-1
+D=M // D = RAM[SP]
+// go to destination
+@LCL
+D=M
+@3
+A=D+A
+M=D // RAM[LCL+3] = D
+```
+
+Do you spot the problem? When we did `D=M` the second time, we overwrote
+our copy of \*SP with LCL. And that's bad, because we will need \*SP on
+the last line.
+
+The solution is a bit convoluted. You need to:
+
+- add 3 to LCL
+- decrement SP by one
+- copy \*(SP) into D
+- go to \*LCL
+- write D into \*LCL
+- subtract 3 from LCL
+
+The main difference is that we no longer touch D as we relay the data. The
+assembly is 14 instructions long, compared to 10 when we do `push local
+3`, as a result of this limitation I call "not enough registers".
+
+I would like to thank John Downey for [his
+implementation](https://github.com/jtdowney/nand2tetris/blob/master/07/commands/pop_command.rb)
+which I referred to when I was stuck.
+</details>
+
+OK, so we're done with pushing and popping. It doesn't sound very
+exciting, does it? After all it's just copying and pasting stuff into more
+stuff. Indeed, we need to discover what we can do with our stack.
+
+## Stack arithmetics
+
+When we program, we will need to do math at one point (gasp, who could've
+thought that). Thus, we will be implementing `add`, `sub` and `neg`. These
+commands do not take "parameters" like push and pop do because they always
+operate on the top of stack. Suppose we have two numbers, a and b, with
+b being the topmost word on our stack, i.e. \*(SP-1).
+
+![SP=259, Stack: 420, a, b](img/nand2tetris_2.1/stack_before_add.png)
+
+After we do `add`, the stack becomes this:
+
+![SP=258, Stack: 420, a+b, b](img/nand2tetris_2.1/stack_after_add.png)
+
+Similar to `pop`, stack arithmetic commands do not erase anything at or
+after the SP marker because the stack machine only cares about what is
+_in_ the stack, i.e. from 256 to SP.
+
+Now it should be clear what `sub` does; instead of a+b, it pushes a-b.
+`neg` only takes one value, so the effect is the top of stack gets
+negated, i.e. b becomes -b.
+
+## Logical commands
+
+I classify the so-called logical commands into two categories:
+
+- Bitwise operators: `and`, `or`, and `not`
+- Comparison: `lt`, `gt`, and `eq`
+
+### Bitwise operators
+
+According to Shocken, logical commands return booleans. There are two
+problems with this statement:
+
+1. Booleans are indistinguishable from integers on the hardware layer;
+2. _How much is a boolean?_
+
+Without thinking I assumed false is 0 and true is 1, as in `stdbool.h`.
+However, upon testing the CPU emulator told me that, in fact, true is -1,
+or 65535. Not a single word was said about this specification in the
+lectures. I suppose the authors had this written in their book somewhere
+but forgot about it in the videos.
+
+<details markdown="1">
+<summary markdown="1">__In hindsight, this is obvious__</summary>
+
+Suppose false is 0 and true is 1:
+
+```
+false = 0000 0000 0000 0000
+true = 0000 0000 0000 0001
+```
+
+Now we do some logical commands:
+
+```
+false & true = 0000 0000 0000 0000 = false
+false | true = 0000 0000 0000 0001 = true
+```
+
+So far so good, but problems arise as soon as we do a bitwise NOT, which
+is the only native instruction related to inverting a boolean:
+
+```
+~false = 1111 1111 1111 1111 = true, probably?
+~true = 1111 1111 1111 1110 = definitely not false
+```
+
+Thus we have proven that, if true is 1, a bitwise NOT does not invert it
+to false. For this to happen, true has to be 16 ones, i.e. -1.
+</details>
+
+### Comparison
+
+We proceed to implement the comparison commands: `lt`, `gt`, and `eq`.
+Apparently they are extremely similar in logic:
+
+- Pop topmost word on stack into D (SP is decremented whenever I say
+ "pop")
+- Pop and subtract the new topmost word from D
+- Compare D to zero
+- Push true to stack if [condition], false otherwise
+
+There are two things to figure out at this moment:
+
+- What is [condition] for each of the three commands
+- How do we make this "if … otherwise …" work
+
+To be honest, I was reluctant to use branching in assembly because if so
+I would have to generate a lot of labels. After quite a while messing with
+the D register trying to find an "elegant" solution, I quit. Branches are
+not that bad.
+
+<details markdown="1">
+<summary markdown="1">__Sample assembly for `lt`__</summary>
+
+Suppose the stack is once again in this state:
+
+![SP=259, Stack: 420, a, b](img/nand2tetris_2.1/stack_before_add.png)
+
+```
+@SP
+AM=M-1 // SP--
+D=M // D = b
+A=A-1
+D=M-D // D = a-b
+M=0 // RAM[257] = false
+@END_LT_0
+D;JGE // if (D >= 0) goto END_LT_0
+@SP
+A=M-1
+M=-1 // RAM[257] = true
+(END_LT_0)
+```
+</details>
+
+## Function commands
+
+No language is practical without functions. Although I cannot be more
+familiar with the notion of functions, the implementation remains
+a mystery. The fascinating thing about functions it that it is not
+a simple goto; the program has to return where it came from. In other
+words, every function call involves a context switch, which will be
+reversed once it returns.
+
+Suppose we are in function f(x), and we're calling g(y). We are going to
+have two states: one used by f just before g is called, and one used by g.
+
+![two blobs labeled "f's state" and "g's state"](img/nand2tetris_2.1/function_states_blobs.png)
+
+What's in a function's state? As it turns out, it's four pointers and one stack:
+
+![f/g's LCL/ARG/THIS/THAT](img/nand2tetris_2.1/function_states.png)
+
+The neat thing about our VM is that, there's only one global stack, and
+the stack of each function just starts somewhere in the middle, instead of
+256.
+
+Suppose I, function f, am about to call function g at a point in time when
+SP=301. I must first push the arguments it needs, let's say 60 and 9:
+
+![SP=303, LCL=299, ARG=292, THIS=1024, THAT=2048, stack (from 301): 60, 9](img/nand2tetris_2.1/stack_before_call.png)
+
+Now, I execute `call g 2`, where the 2 is the number of arguments that
+g takes. But before I do anything risky, I need to back up my state. One
+of them is the "return address", which is the address of the instruction
+I would be executing next if I didn't call g.
+
+<details markdown="1">
+<summary markdown="1">__Wait, you're telling me we're predicting the future?__</summary>
+
+It sounds like wizardry but it's not. This is done with the assembler's
+symbol substitution: I insert a label (for example, `(Main$ret.0)`) just
+before the first instruction of the next VM command. This is where I'll
+end up if `call g 2` wasn't there. Knowing the assembler will take care of
+this, I just write:
+
+```
+@Main$ret.0
+D=A
+@SP
+A=M
+M=D // *SP is the return address
+@SP
+M=M+1 // SP++
+// more code
+@Main.g
+0;JMP
+(Main$ret.0)
+// assembly translated from next VM command
+```
+
+If `0;JMP` is instruction 419, then the return address pushed is 420.
+</details>
+
+Then we just push LCL, ARG, THIS and THAT:
+
+![SP=308, LCL=299, ARG=292, THIS=1024, THAT=2048, stack (from 301): 60, 9,
+420, 299, 292, 1024, 2048](img/nand2tetris_2.1/stack_call_backup.png)
+
+The section of memory from 303 to 307 (inclusive) is called the "frame" of
+f, because it contains all the information needed by f to recover its
+state.
+
+Now that the clones of LCL et al. are safely contained within the frame,
+we can commit all sorts of atrocities to them. ARG will become the address
+of the first argument pushed onto f's stack, namely 301; LCL will become
+the first address after the frame ends. We will not be touching THIS and
+THAT just yet.
+
+![SP=308, LCL=308, ARG=301, THIS=1024, THAT=2048, stack (from 301): 60, 9,
+420, 299, 292, 1024, 2048](img/nand2tetris_2.1/stack_after_call.png)
+
+At this point f is ready to jump to g. Suppose function g begins with this
+VM command: `function g 3`. The 3 here is not the number of arguments.
+Instead, it's the number of local variables it needs. Therefore, this VM
+command pushes this number of zeroes onto the stack:
+
+![SP=311, LCL=308, ARG=301, THIS=1024, THAT=2048, stack (from 301): 60, 9,
+420, 299, 292, 1024, 2048, 0, 0, 0](img/nand2tetris_2.1/stack_after_function.png)
+
+From now on, RAM[311-2047] is the stack that g has access to. It can also
+access `argument 0-1` and `local 0-2`.
+
+A dozen commands later, g decides to return. First it copies its topmost
+word on stack — the return value — to somewhere convenient for f to use.
+Then it exits and hands the control back to f. But in fact, g has no idea
+it came from f; it only knows where to find and recover the pre-saved
+context.
+
+Suppose g pushed a few things and touched some local variables, and
+decides to return.
+
+![SP=313, LCL=308, ARG=301, THIS=1024, THAT=2048, stack (from 301): 60, 9,
+420, 299, 292, 1024, 2048, 69, 51, 0, 2, 3519](img/nand2tetris_2.1/stack_before_return.png)
+
+The return value is 3519, and we know that before g was called SP was 301
+— always equal to our current ARG. So we copy \*(SP-1) to \*ARG.
+
+When the number of arguments is zero, \*ARG happens to be where we store
+the return address, namely 420. We could overwrite it and lose context!
+That's why we need to back up the return address somewhere safe and
+recover from there; I selected R13 in my implementation and I've had no
+problems yet.
+
+After this is done, everything after LCL is useless. So we move SP to LCL:
+
+![SP=308, LCL=308, ARG=301, THIS=1024, THAT=2048, stack (from 301): 3519, 9,
+420, 299, 292, 1024, 2048, 69, 51, 0, 2, 3519](img/nand2tetris_2.1/stack_before_restore.png)
+
+Then, we restore LCL et al. by popping them one by one, and move SP right
+after the return value.
+
+![SP=302, LCL=299, ARG=292, THIS=1024, THAT=2048, stack (from 301): 3519, 9,
+420, 299, 292, 1024, 2048, 69, 51, 0, 2, 3519](img/nand2tetris_2.1/stack_after_return.png)
+
+Finally, we jump to the address indicated by R13, which is 420. And thus,
+this grand waltz of function call and return is complete.
+
+## Writing the VM translator
+
+As a person with limited wits and patience, I decided to write the VM
+translator in Python 3.10 to leverage the power of pattern matching, aka
+`match case`, as defined in [PEP 636](https://peps.python.org/pep-0636/).
+This means I can match VM commands like
+
+```
+match cmd.split():
+ case []:
+ continue
+ case [("push" | "pop") as action, segment, index]:
+ # handle memory command
+ case [("add" | "sub" | "neg" | "and" | "or" | "not") as command]:
+ # handle arithmetic / logical command
+ # more cases here
+ case _:
+ # invalid command, raise error
+```
+
+This level of syntactic elegance is, in my opinion, unmatched among all
+languages (pun intended). One other characteristic of the VM translator is
+that it's context-free. In contrast, the assembler I wrote in project 06
+needs to read the entire file to decide whether a given symbol is
+a reference to a label or a variable. However it does depend on _some_
+global state, for example to generate unique labels for each `lt` command.
+
+Over the course of implementation, I realized that the hardest part is
+that you are programming in two languages at once. You are using one
+language to generate another. Of course, assembly is way harder (I forgot
+`A=M` _multiple times_, and instead of debugging for hours in the CPU
+emulator you should probably recheck if your assembly matches your
+pseudocode in terms of pointer dereferencing). But the caveats in Python
+showed up in unexpected places. For example, my optimized implementation
+of `function <name> <varc>` consists of three parts: snippet A, snippet
+B repeated (varc - 1) times, and snippet C. As you can imagine, things
+took a dramatic turn as varc approaches zero. It turns out Python does not
+raise an exception when I do `snippet_b * (-1)`; it silently returns an
+empty string, which means at least one variable is initialized no matter
+what. The bug is fixed, but I learned one more reason why Python is weird.
+
+### Code statistics
+
+- 408 lines of code
+- About half of it is multiline strings of assembly
+
+## Is the stack machine any good?
+
+The stack machine, or any kind of virtual machine used by industrial
+languages, is an intermediate step from high-level source code to machine
+code. Not every languages needs it, though; C is a counterexample. Virtual
+machines offer you one more level of abstraction, at the cost of
+efficiency and size.
+
+<details markdown="1">
+<summary markdown="1">__One example how VM increases executable size__</summary>
+
+Let's say we need to do:
+
+```
+push constant 1337
+pop local 69
+```
+
+Our VM translator would output these lines:
+
+```
+// push constant 1337
+@x
+D=A
+@SP
+A=M
+M=D
+@SP
+M=M+1
+// pop local 69
+@69
+D=A
+@LCL
+M=M+D
+@SP
+AM=M-1
+D=M
+@LCL
+A=M
+M=D
+@69
+D=A
+@LCL
+M=M-D
+```
+
+This is 21 instructions, but in reality, some instructions are totally
+unnecessary. Like when we incremented, then decremented SP. If we treated
+the two commands as one atomic operation and rewrite the assembly from
+scratch:
+
+```
+@69
+D=A
+@LCL
+M=M+D
+@1337
+D=M
+@LCL
+A=M
+M=D
+@69
+D=A
+@LCL
+M=M-D
+```
+
+Down to 13 instructions now.
+
+</details>
+
+The merit of a half-human, half-machine abstraction is increased control
+and security. Suppose a function declares itself as `function f 4`, but
+asks us to `pop local 9`. The VM translator could, in theory, refuse to
+assemble the code, which could be malicious (if it even runs, that is).
+But my VM translator does not, because I want to keep my VM translator as
+simple as possible. Furthermore, the VM acts as a better troubleshooting
+tool than assembly for your compiler.
+
+## Conclusion
+
+In project 07 and 08 of nand2tetris part 2, we built a VM translator that
+translates an imaginary architecture to a concrete one. This added layer
+of abstraction bridges the gap between high-level source code and machine
+language. In this course, we discussed the benefits and costs in the
+Perspectives unit; however, specifics are out of scope.
+
+Philosophically, the lesson I learned is that in modern computer
+engineering, universality often beats efficiency. The stack machine is
+clever in that it is a complete abstraction of assembly. In other words,
+everything you expect a computer to do, if you don't care about speed, can
+be done on a stack machine, without the need to write a line of assembly.
+Incidentally, this also describes high-level languages. Efficiency is
+lost, yes, but there exist two justifications:
+
+1. Most applications are not efficiency-critical, and if yours is
+ extremely so, you probably shouldn't be using a VM;
+2. If you _really_ need efficiency on VM, you can revise your translator
+ to generate more efficient assembly.
+
+I would like to say the following about this course:
+
+- Shocken spoke in _every_ single lecture. Impressive.
+- I'm not sure if I'm the only one, but I find the video lectures a lot
+ more repetitive than those in part 1.
+- At least one technical specification (booleans) was not communicated
+ well.
+- The project is arguably more challenging (intellectually, not
+ syntactically) than writing the assembler.
+
+That's all for project 07 and 08; see you in Part 2.2!
diff --git a/docs/projects/nand2tetris_2.2.md b/docs/projects/nand2tetris_2.2.md
new file mode 100644
index 0000000..198c048
--- /dev/null
+++ b/docs/projects/nand2tetris_2.2.md
@@ -0,0 +1,178 @@
+# nand2tetris, Part 2.2
+
+2022-09-14
+
+Welcome back! In Part 2.2 we are going to start writing a compiler. It
+compiles a high-level language called Jack to VM commands we saw in the
+[last chapter](nand2tetris_2.1.md). For ease of organization I decided,
+once again, to write it in Python.
+
+The compiler is a three-step process. In this blogpost we are writing
+the first one — a tokenizer. What does a tokenizer do?
+
+## Tokenizer
+
+Say we have a file called "Main.jack", containing only this line:
+
+```
+class Main {}
+```
+
+The tokenizer is going to read it, then extract meaningful elements or
+"tokens" one by one until the file is exhausted. For this file, the tokens
+will be `class`, `Main`, `{` and `}`.
+
+But how? I've never taken a compiler course before, so I was at a complete
+(compilete?) loss.
+
+I was tempted to say, "oh simple. I can just split the file and tokenize
+each element from what it looks like." But split by what? Whitespace? No,
+because you see no whitespace between the curly braces. And while you
+could traverse it char by char, efficiency is up to debate.
+
+<details markdown="1">
+<summary markdown="1">__I considered this a while back. Here's how I would have done it__</summary>
+
+Suppose I'm inside the function that reads the file char by char. I'm
+going to define a list of tokens, let's say instances of the class
+`Token`, like this:
+
+```
+tokens = []
+```
+
+Then I will implement method `Token.append_char(char: str)` that takes one
+character, and:
+
+- append it to itself if it is allowed (e.g. `e` after `someVariabl`)
+- do nothing if the character doesn't matter (e.g. a space after a paren)
+- signal its termination if the char ends the token and starts another
+ (e.g. `+` after a variable)
+- raise an error if the token is not allowed at all (e.g. `a` directly
+ after `123`)
+
+This way when we read a `char`, we try `tokens[-1].append_char(char)` to
+see if the last token we saw accepts it. If not we just end the previous
+token, and push the new one to the list of tokens.
+
+You can see how this is an efficiency disaster. So many function calls are
+unnecessary if we could just look ahead more characters at a time.
+
+</details>
+
+For this very concern, I rejected this char-by-char idea in favor of
+line-by-line. The solution is, I feed the line into a function which
+extracts and returns the first complete token. Then I ask the returned
+token how many characters long it is. If it answers `n`, I remove the
+first `n` characters (and leading whitespace) from the line, then feed it
+back into the same function again. Repeat this until the line has nothing
+left, and I will get a list of tokens.
+
+I took advantage of certain rules in the Jack language to simplify things.
+For instance all symbols are 1 character long. No `==`, not even `<=`,
+only these:
+
+```
+{}()[].,;+-*/&|<>=~
+```
+
+This means if the first character is in the list of symbols, that's gotta
+be it; we can stop looking.
+
+If the first character is a digit, then the token is no doubt an integer
+literal. If it is a letter or underscore, we first check if it's
+a keyword; if not it must be an identifier, e.g. class name, variable,
+etc.
+
+And there are strings. The Jack standard specifies a string literal to be
+"zero or more non-double-quote characters surrounded by double quotes"
+(paraphrased). Easy, regex go brrr: `(".*?")`
+
+The `*?` signals a non-greedy search. For example, suppose we have this
+string:
+
+```
+"string 1".append("string 2")
+```
+
+and match it against these two regexes:
+
+```
+(".*") -> "string 1.append("string 2"
+(".*?") -> "string 1"
+```
+
+The second one is correct.
+
+<details markdown="1">
+<summary markdown="1">__What about escape sequences?__</summary>
+
+Canonically, Jack does not support them. A string ends whenever a double
+quote is met. I don't like it. I want escape sequences. I _need_ escape
+sequences.
+
+And thus I defined a "language extension" which is opt-in through argument
+`-E escape`. The implementation is as simple as changing the regex, but it
+took me a while to figure out.
+
+The regex is `("(.|\\")+?[^\\]")`. Let's take it apart:
+
+```
+( ) group 1
+ " literal "
+ ( )+? group 2, one or more, non-greedy
+ . any character
+ | or
+ \\" \"
+ [^\\] any character except \
+ " literal "
+```
+
+It will keep reading the line until it meets an unescaped double quote.
+Problem solved.
+</details>
+
+When a string is done, we come back to the main tokenizer function and
+look for the next token. And this is how I tokenize the source code line
+by line.
+
+A small caveat. There are multiline comments in Jack that could mess
+things up if I read line by line naïvely. I have to keep a flag in the
+scope of this file so I know if I'm inside one or not.
+
+## Error handling
+
+A challenge I set for myself is to write sensible error messages.
+Something to alert the Jack programmer (who, too, is me). Therefore
+whenever I instantiate a token, I pass the current line number and the
+position of the token on that line. Whenever the tokenizer fails to
+recognize something, it will put a caret under it:
+
+```
+$ cat bad.jack
+class Main {
+ hehe I don't give a shit
+}
+
+$ python -m hackc bad.jack
+bad.jack:2
+ hehe I don't give a shit
+ ^ Invalid token
+```
+
+Caveat: it only works for indentation with spaces for now because the
+position in line is counted with respect to bytes, not columns. It _is_
+possible to change this behavior.
+
+## Conclusion
+
+The tokenizer is the first step from source code to executable programs.
+It bridges the gap between human and machine, and thus involves the most
+"stupid" intricacies. However, as soon as we get this over with, the rest
+is constructing a syntax tree from the list of tokens with regard to a few
+rules.
+
+You can check out my code here:
+[hackc](https://git.fkfd.me/nand2tetris.git/tree/projects/hackc)
+
+See you in part 2.3!
diff --git a/docs/projects/reflow-workshop.md b/docs/projects/reflow-workshop.md
new file mode 100644
index 0000000..7cb9ed1
--- /dev/null
+++ b/docs/projects/reflow-workshop.md
@@ -0,0 +1,792 @@
+# Reflow Workshop: A Journal
+
+Status: updating
+
+## 2023-03-15, Wednesday
+
+Today I took a group interview for assistants of a new hackerspace. Five
+students were present, in a room with Mr. Xiao, the manager.
+
+My prior work experience bought me some credibility, and I shared with
+Xiao my frustration not being able to properly do electronic engineering
+for lack of resources.
+
+We then took a walk in the empty hackerspace. The proper name is actually
+Tang Junyuan Student Innovation and Entrepreneurship Center, but I prefer
+hackerspace.
+
+![A two-story hackerspace. Empty save for workbenches, chairs and
+cabinets.](img/reflow-workshop/empty_tjy.jpg)
+
+▲ This is only half of the hackerspace.
+
+We had a heck of a time discussing what this space is potential of.
+Four-wheelers. LaTeX and vim workshop up the stairs. And of course
+— reflow soldering.
+
+Reflow soldering is on my [list of life goals](../random/life_goals.md), but
+it's in my dream since 2019. At that time I was a nobody in high school.
+All I could solder was through-holes. Kliment, my IRC friend (though we've
+met IRL once) kindly mailed me one of his works: [an electronic
+kitten](https://github.com/kliment/catws). It is 5cm×5cm.
+
+![Picture of cat on black PCB. The eyes are glowing
+blue](img/reflow-workshop/electronic_cat.jpg)
+
+▲ It purrs when you stroke it right; otherwise, it hisses.
+
+## 2023-03-16, Thursday
+
+So I revealed my plans to Kliment, who was very supportive as usual. He
+forwarded me an email listing the things I need.
+
+One crucial thing is that I have to program the <abbr
+title="microcontroller unit">MCU</abbr> before soldering, so I need
+a <abbr title="Quad-Flat No-leads with 32 pads">QFN32</abbr> test socket.
+Kliment estimated it to cost 800 RMB, but in China it seems to only be
+~200. I bought one for 180.
+
+## 2023-03-17, Friday
+
+Just ordered the two parts from LCSC with specific numbers: Murata
+PKMCS0909E4000-R1 and Meihua MHS110FRGBCT. LCSC gave me a 20 RMB coupon.
+
+After that I went to look for the MCUs (ATtiny88-MU) and found them in
+a random Taobao store that sells them at 7 RMB apiece. I think I might
+need other chips later, so I also added some ATtiny48 and ATmega328PB to
+my shopping cart. I asked customer service if they got any in stock. They
+said I could just place my order, so I did.
+
+Then I went to hunt for the PCB and stencils. Kliment forewarned me that
+factories use only one stencil per assembly line, so my use case is
+extremely rare. I will have to consult the manufacturer for a quote.
+
+## 2023-03-19, Sunday
+
+Talked to the sales rep of JDBPCB, they were confused for a good minute,
+then realized what I was asking for. They told me to just place my order.
+They claim they had seen this kind of thing before. Kliment suggests it
+might have been [Honza Mrazek](https://honzamrazek.cz/).
+
+Sent <abbr title="basically a zip file of everything the factory needs to
+produce PCBs and stencils">Gerbers</abbr> to them, along with my requests
+in the notes. They PCBs are approved.
+
+## 2023-03-20, Monday
+
+I received the piezo speaker and RGB LEDs from LCSC today.
+
+To my surprise JDB approved my stencil request, and priced it at an
+unexpectedly low 80 RMB.
+
+Later the customer service approached me to confirm once again that
+I wanted 10 copies of the same stencil. Upon hearing yes, they revealed
+that the 80 RMB was actually the price for *one* stencil, but for me they
+could do 216 RMB for 10, tax included, with the implication that I do not
+do this kind of shit to them again. They fear me.
+
+Kliment helped shave off 46 RMB off my budget. I was looking for
+a hotplate which he said will not cost more than 80 RMB. When I told him
+best I could find is 135, he said I was looking for the wrong thing; what
+I need is not a hotplate advertised for solder work, but a consumer grade
+for heating food and beakers. I changed the keyword and found one at 89.
+
+## 2023-03-21, Tuesday
+
+Cell batteries, holders, and the QFN32 socket have arrived.
+
+![QFN32 socket with lid open](img/reflow-workshop/qfn32_socket.jpg)
+
+▲ \*Kneels on floor\* \*Unlids socket\* \*Presents brand new ATtiny88-MU\*
+Will you marry me?
+
+The socket is a delicate piece of hardware. You would place the MCU in
+a 5mm×5mm slot where two adjacent pins are 0.5 mm apart, and access them
+from a <abbr title="Dual In-line Package">DIP</abbr> interface where that
+distance (also known as the lead pitch) is 2.54 mm.
+
+The microcontrollers on the other hand haven't even begun shipping yet.
+I asked them what's wrong, and their answer was: "we don't have them."
+
+…Then don't sell them?
+
+I then asked when they will be back on stock. They read my messages but
+remained silent. Chances are they will never ship, says Kliment. This is
+just a scam on a chain of fake distributors. The evening they finally gave
+in, offering a refund.
+
+At the same time I'm looking for more trustworthy vendors. Kliment
+referred me to Winsource. Although a Shenzhen company, they don't seem to
+ship to China under that brand. So I reverse engineered a bit to find that
+its Chinese trademark is 聚源鑫. Small company, not super well-known, not
+even a store on Taobao. I'll talk to them in working hours tomorrow.
+
+I am currently inside an episode of self-doubt. What if I fail to learn
+reflow myself? Even if I can barely manage, what qualification do I have
+to teach other people? Perhaps I should have picked my battles and went
+for a simple SMD hand soldering workshop instead. That's much much easier,
+and I have done this three times.
+
+## 2023-03-22, Wednesday
+
+As I rose from bed I realize I could have ordered 5 stencils, and
+organized two or more sessions in series. But I guess 10 is ok. Especially
+when imbursement is possible.
+
+Was busy most of the day, and I forgot to contact that shady vendor.
+
+After dinner I moved what I have got so far to the hackerspace with my
+bicycle.
+
+![Three boxes in my front basket and one big box mounted on the rack with
+tape](img/reflow-workshop/bicycle.jpg)
+
+▲ It was a 2.5 km ride.
+
+In the empty hackerspace I unboxed the hotplate:
+
+![A black circular pad on top of a yellow metal base. On the side is
+a knob](img/reflow-workshop/hotplate.jpg)
+
+▲ They sent along a *physical* invoice, a rare item these days
+
+## 2023-03-23, Thursday
+
+I rang 聚源鑫 to ask about the availability of ATtiny48-MU, the cheaper
+alternative to 88. They don't have any.
+
+Now that the hope of getting chips from shady sources has vanished,
+I returned to LCSC to check out their offers. They are able to act as
+a broker between me and Mouser, charging 12.87 RMB apiece. Sounds good.
+I ordered 20.
+
+## 2023-03-24, Friday
+
+The much awaited stencils are here! They were sandwiched between two
+pieces of pretty rigid cardboard. The PCBs are in the same box.
+
+![Shiny stencils taped on cardboard, with black PCBs placed on
+top](img/reflow-workshop/stencils.jpg)
+
+▲ For scale, each stencil is 5cm×5cm
+
+## 2023-03-25, Saturday
+
+The major chunk of my BOM is taken care of. Now we shall focus on the
+nitty bitty discrete parts, such as capacitors and resistor packs. As
+always they are extremely cheap.
+
+Last time I forgot to give LCSC the invoice info eligible for
+reimbursement, so this time I ordered 20 more piezo speakers with the
+info. Totally necessary and not abusing the hackerspace funding.
+
+## 2023-03-26, Sunday
+
+The discrete parts have arrived.
+
+Also I brought all the tools I have from home: multimeter, soldering iron,
+solder, flux, etc.
+
+## 2023-03-27, Monday
+
+Mouser seems to have shipped the MCUs.
+
+In other news, I found this beautiful graphic that would make a great
+poster background:
+
+![Microscopic anime girls placing SMD parts on a blue
+PCB](img/reflow-workshop/anime.jpg)
+
+▲ Credits: [Shapo on pixiv](https://www.pixiv.net/en/artworks/91199411)
+
+I made a mistake. On Saturday I thought a friend had spare LEDs from
+a keyboard workshop last year so I didn't order any. But turns out I got
+the size (monumentally) wrong, they're actually 3528 instead of the 0603
+I want. (Makes sense though; we soldered them for backlight and 0603's
+would be barely visible.) So I ordered 100 along with 10 pairs of
+tweezers.
+
+## 2023-03-29, Wednesday
+
+The tweezers are here.
+
+## 2023-03-30, Thursday
+
+So are the LEDs.
+
+## 2023-04-01, Saturday
+
+I ordered 10 tin scrapers. Fun fact: in German they're called
+["Japanspachtel"](https://de.wikipedia.org/wiki/Japanspachtel) (der
+Japanspachtel, plural is die Japanspachtel) to distinguish from regular
+[Spachtel](https://de.wikipedia.org/wiki/Spachtel_(Werkzeug)).
+
+Also, I almost ended up buying the wrong kind of chemical. What I need is
+isopropyl alcohol, but "industrial alcohol" on the marketplace refers to
+methanol. The customer service kindly suggests I look for "industrial
+ethanol". I ordered 500 mL.
+
+## 2023-04-02, Sunday
+
+Today I made what I consider one of my best designs with Inkscape.
+
+!["TECHJI REFLOW WORKSHOP", but the "O" in "REFLOW" is shaped like an IC
+chip and there's a via next to the
+"W"](img/reflow-workshop/reflow_workshop_title.png)
+
+▲ The font is Orbitron.
+
+## 2023-04-03, Monday
+
+The Japanspachtel are here. I headed to the hackerspace and did a partial
+test run. I attached the stencil and, with the most sloppy skills
+possible, literally smeared solder paste wherever there are holes.
+
+![Stencil and PCB held together with bolts and
+nuts](img/reflow-workshop/bolted_stencil.jpg)
+
+▲ Before applying the paste.
+
+Upon removal, there were quite a lot of shorts, but easily fixable with
+the sharp edge of the Japanspachtel.
+
+![Grey gooey solder paste over
+pads](img/reflow-workshop/partial_test_run_paste.jpg)
+
+▲ Most of the tiny displacements will fix themselves once heated.
+
+Then I placed all the capacitors, resistor packs and LEDs with tweezers.
+Finally it's time to heat it. I cranked up the hotplate, but _boy_ how
+does it get hot _so_ fast. A minute and it's already 250 C. Way too high
+than needed. I cooled it down to ~180 C and shoved down the PCB.
+
+![PCB on the hotplate](img/reflow-workshop/partial_test_run_hotplate.jpg)
+
+▲ I forgot to place D3. Whoops.
+
+I forgot to bring this up, but the solder paste I bought is not the
+regular kind. Instead, it's Sn42Bi58 (42% tin and 58% bismuth), the same
+kind Kliment used for his workshop. The bismuth makes the melting point
+really low, as low as 138 C.
+
+It only took moments for the fume to come out, and here we have our solder
+joints.
+
+![Silver solder joints on pads connecting parts to PCB. A QFN32 footprint
+is left unpopulated](img/reflow-workshop/partial_test_run_joints.jpg)
+
+▲ These joints look solid, or so I hope? At least they don't jiggle.
+
+I'm surprised it went so well. I mean, not up to my finest standard, but
+it's really more than I expected from so little effort.
+
+In the evening, I received the ethanol.
+
+## 2023-04-04, Tuesday
+
+With the ethanol I got yesterday I wiped the stencil and Japanspachtel
+I was using. Good as new.
+
+## 2023-04-05, Wednesday
+
+Apparently my ATtiny's arrived in Shenzhen yesterday! Any day now…
+
+## 2023-04-08, Saturday
+
+They're here! Sadly I don't have time to play with them, I need to go to
+a barbecue…
+
+## 2023-04-09, Sunday
+
+Guess whose dream four years ago came true today!
+
+I can hardly contain my excitement, but I'll try my best to describe what
+I did just now:
+
+### 20:00
+
+I arrived at the hackerspace and set everything up. I was alone so there's
+no pressure. I think I might be able to finish this by ten.
+
+### 20:10
+
+I carefully picked up one (1) ATtiny48-MU chip and placed it inside the
+QFN socket. It looks like this:
+
+![Loaded QFN socket](img/reflow-workshop/qfn32_socket_loaded.jpg)
+
+▲ Finally, I can propose to my cyberfiancé(e)
+
+I closed the lid and taped it shut. I don't want to lose it!
+
+### 20:25
+
+The chip must be programmed before going onto the PCB. For this I'm using
+a [USBasp](https://www.fischl.de/usbasp/).
+
+![QFN socket on a breadboard, connected to a USBasp with jumper
+wires](img/reflow-workshop/usbasp.jpg)
+
+▲ Actually there are mistakes in this photo. Three consecutive pins are
+off-by-one.
+
+After that, I connected the USBasp to my computer and attempted to
+communicate with the chip, but failed. By continuous trial-and-error,
+I found and fixed three problems:
+
+- the MOSI, MISO and SCK pins are off-by-one
+- I didn't pull `~RESET` low
+- The baud rate is too high
+
+![Screenshot of avrdude in interactive mode](img/reflow-workshop/avrdude.png)
+
+▲ Finally, we have established diplomatic relationship with the Silicon
+World.
+
+I then set the fuses so that it runs at 8 MHz. And… it's done I guess?
+
+### 21:10
+
+Then comes the exciting part. I need to repeat what I did on
+[2023-04-03](#2023-04-03-monday), but now we're going full throttle. I now
+have a chance to capture photos I forgot by then.
+
+![A Japanspachtel on top of a stencil and PCB](img/reflow-workshop/japanspachtel.jpg)
+
+![Stencil with paste scraped all over it](img/reflow-workshop/solder_paste_scraped.jpg)
+
+### 21:30
+
+Doing the QFN32 is harder than I thought. There's always too much paste.
+At first I thought it was my recklessness, so I kept reworking with
+increasing care. It took me three tries to realize it is not. Actually, it
+was the tiny displacements of the stencil as I scraped the other parts.
+
+![Close-up of the QFN32 footprint with too much
+paste](img/reflow-workshop/qfn32_paste_short.jpg)
+
+▲ All the pads make one single blob. Not ideal.
+
+So, instead of scraping the QFN32 footprint first, in my fourth run
+I scraped it _last_. But upon removal there was too little. Time was
+running out, so in a risky move I re-attached the stencil and scraped
+a little more paste.
+
+![Close-up of the QFN32 footprint with a reasonable amount of
+paste](img/reflow-workshop/qfn32_paste_success.jpg)
+
+▲ It worked!
+
+So after manually separating a few shorts on the discrete components, it's
+time to pick & place!
+
+### 22:00
+
+![All the parts placed on respective
+footprints](img/reflow-workshop/pick_and_place.jpg)
+
+▲ Didn't lose D3 this time
+
+Onto the hotplate!
+
+### 22:10
+
+Watch C2 fall in place as the solder heats up.
+
+<video controls>
+ <source src="../img/reflow-workshop/cap_reflow.mp4" type="video/mp4">
+</video>
+
+▲ MP4, 4.1 MiB, no sound
+
+The only problem was two shorted leads, which are easily fixed with flux
+and the soldering iron.
+
+![Two leads are shorted on the MCU](img/reflow-workshop/qfn32_joint_short.jpg)
+
+### 22:20
+
+BEHOLD
+
+THE FIRST PROTOTYPE
+
+![Completely populated PCB, LED is on](img/reflow-workshop/first_prototype.jpg)
+
+▲ Everything works as intended
+
+![Two PCBs, cat side up](img/reflow-workshop/kliments_vs_mine.jpg)
+
+▲ Kliment's electronic kitten on the left, mine on the right
+
+I spent another ~20 min cleaning up the workbench, then left.
+
+## 2023-04-10, Monday
+
+Now that the prototype turns out a success, I can finally tell JDB to
+produce more PCBs. Interestingly, the first batch of five cost 90 RMB, but
+the second batch of 20 cost only 111. The more boards you order, the
+cheaper each one is.
+
+I then ordered a box of Kimtech lint-free wipes. They are much cheaper
+than I thought.
+
+## 2023-04-11, Tuesday
+
+I ordered 5 magnifying glasses and 5 spray bottles.
+
+## 2023-04-12, Wednesday
+
+Both the magnifying glasses and spray bottles are here.
+
+## 2023-04-15, Saturday
+
+I got the 20 PCBs and the invoice for everything I ordered from JDB.
+
+## 2023-04-26, Wednesday
+
+After a week of final exams, I took a few days off to relax, before
+rediscovering the existence of reflow workshop.
+
+It's spring break right now and I'm at home. At this point everything
+seemed ready, but an inventory check reported a lack of cell batteries and
+holders. So I ordered a bunch more.
+
+## 2023-04-28, Friday
+
+I went back to uni to pick up the package. I also went to two concerts
+where I caught a guitar pick.
+
+## 2023-05-06, Saturday
+
+I discussed workshop arrangements with my colleagues at TechJI. It will be
+held twice on both evenings this weekend.
+
+## 2023-05-07, Sunday
+
+Behold, the final poster design:
+
+![From top to bottom: anime girls pic, "TECHJI REFLOW WORKSHOP", PCB
+renderings, 5/13-14 18:00 唐君远, and credit for the
+illustration](img/reflow-workshop/poster.png)
+
+▲ Despite the visual effect, most of the graphics is not my own.
+
+## 2023-05-08, Monday
+
+I drafted the promotional article to be published on WeChat.
+
+## 2023-05-10, Wednesday
+
+### Afternoon
+
+The article is published, but WeChat decided that hyperlinks are not
+allowed. Among them is the survey we use to register participants.
+Sabotaged by WeChat's pointless restrictions.
+
+I received messages asking for the link, but I can't edit the article
+(thanks WeChat), nor can I post it in the comments without approval (many
+fucking thanks WeChat). I ended up urgent pinging the admin, who
+thankfully made it public real fast.
+
+The moment link went public applications blew up. In two hours we had 32
+applicants. We can seat at most 19, because we only have so many ATtiny's
+left. This means I'll have to send rejection letters to so many people.
+I've always hated rejection letters. Now I've become what I swore to
+destroy.
+
+Conclusion: Tencent is a horrible asshole whose sole purpose is to destroy
+the open internet.
+
+### Evening
+
+I made a horrible mistake. I made applicants fill in their cell number,
+and stated that I will contact them via SMS or WeChat. HUGE mistake. I had
+to manually copy the numbers to my phone. Should have asked for email
+instead.
+
+## 2023-05-11, Thursday
+
+I went to print the poster on an A2 sheet of paper. It's pretty!
+
+![Poster in a giant printer](img/reflow-workshop/poster_printing.jpg)
+
+▲ The exposure is _just_ long enough to blur the print head, which I think
+makes the photo way more dynamic
+
+I cycled to the hackerspace to hang it up. It's much smaller than all the
+other posters, but it's hands down the prettiest.
+
+After that we had an internal training session within TechJI. The purpose
+is to familiarize ourselves (including me, because the last time I did
+this was one month ago). We started with an overview, then we smeared
+solder paste. Everyone had their own technique. The common trend is too
+much paste, and/or going over one place too many times. Everyone had to
+rework at least once, because that is essential knowledge for the
+workshop.
+
+Who could apply paste the best? We have a winner!
+
+![Paste in the right places on the QFN
+footprint](img/reflow-workshop/perfect_qfn.jpg)
+
+▲ This is a sign that they are better at it than I am
+
+Up next we had pick-and-place. Sadly we can't offer a complete kit to
+everyone because we need to leave enough for the 19 people. Two of us were
+actually among the 19, so we decided to make two prototypes.
+
+I demonstrated C1 and C2 (the two 0805 caps), and made a few comments on
+the polarity of the diodes, pins of the QFN, and the 0402 region where you
+make a recognizable pattern so you know which board is yours on the
+hotplate.
+
+No one had any trouble. I inspected the result which is shockingly great,
+then turned on the heat.
+
+One of them is observed to have two shorts on RN2, and the other is
+flawless. However, when we put a battery in it, the RGB LED (D6) doesn't
+turn on. Why not? It turns out D6 was upside down. It is indeed very
+subtle, and even I failed to catch that before heating. Then I noticed the
+same problem on the other board. With a soldering iron I manually fixed
+them.
+
+It's time to test our products. Weirdly, one works, but the other is
+really bad-tempered for some reason. Whenever you remove your finger from
+the battery, it hisses nonstop. I would advise it to see a therapist.
+
+I asked Kliment what could be the reason, and he suggested it was either
+a short on the MCU or the resistor packs, or a shoddy battery holder,
+because the kitten hisses when reset.
+
+To my disappointment I only ordered 100 LEDs, which is _barely_ enough.
+And to an electronic engineer _barely_ enough is not enough. We have to
+keep a redundancy, so I ordered 200 more.
+
+## 2023-05-13, Saturday
+
+Just finished the first session and I am so fucking exhausted. Three
+hours, working nonstop. I forgot where I left my bag so I couldn't even
+show the slides I made.
+
+Long story short, we instructed 8 terrified beginners how to do reflow.
+Out of all participants, one managed perfect paste on the first try; most
+made it in three.
+
+Here's some of the jobs that made me go "hmm that's not bad":
+
+![Four decent QFN32 paste jobs](img/reflow-workshop/qfn32_paste_sat.jpg)
+
+We began toastin' at exactly the two hour mark.
+
+![Two PCBs on a hotplate](img/reflow-workshop/hotplate_sat.jpg)
+
+▲ One core, two threads
+
+![Fully assembled board](img/reflow-workshop/product_sat.jpg)
+
+▲ Typical board right after heating
+
+I then spent like half an hour manually fixing shorts, reinforcing the
+battery holder, etc. Everyone's electronic kitten purrs and hisses, but
+not every LED turns on. It could be a fault in the touch sensing area,
+MCU, resistor pack, or the LED itself. I did not have the time to diagonse
+one by one. On one board this happened on the tail, which makes the kitten
+immune to tail pats, and consequently really tame and hardly hisses.
+
+Everyone got their product at 21:something and I cleaned up the place and
+wiped every tool until minutes to 22. It's a miracle we finished the whole
+thing before 22:00, especially when the staff is made up of three
+ex-terrified beginners, two of whom literally learned reflow 48 hours ago.
+
+![wojak meme. people who come to workshops: upper class noblepeople;
+people who make workshops: exhaused, messy hair, drinking
+Monster](img/reflow-workshop/people_who.jpg)
+
+▲ This is honestly how I feel
+
+Later, I found my bag in another room.
+
+## 2023-05-14, Sunday
+
+OK today I put my bag in the right place. And we have more tables to sit
+around. This is good because I don't have to run around all the time.
+I also got to use a giant LCD screen to project slides on. Overall
+a significant improvement.
+
+Today we have 9 people coming. That's one more than yesterday, but we're
+more prepared than ever.
+
+One person did a perfect paste job in one go; most did 2, 3, or 4. For
+some reason the people today are really into reworking. One of them
+reworked a near-perfect board only to end up with the same thing. But hey,
+practice makes near-perfect.
+
+This trend of endless reworking brought us behind schedule. We expected to
+move on to pick & place at 19:30, but didn't manage until 20:something. At
+least they're having fun??
+
+I really should have forbade reworking once it's good enough.
+
+Introducing today's contenders in the QFN arena:
+
+![Four decent QFN32 paste jobs](img/reflow-workshop/qfn32_paste_sun.jpg)
+
+The first one is the one-go job. Pads 27-31 didn't seem to catch any
+paste, but that's fine because we can fix that at the end. The
+bottom-right is an outright expert. They somehow managed to separate
+_every single pad_.
+
+This person's job overall is also impressive:
+
+![Paste job with very few defects and clean outline on every
+pad](img/reflow-workshop/paste_best.jpg)
+
+▲ D6 and the 0402 region are _flawless_
+
+Pick and place is easy. And since I have slides to help me today, it was
+a piece of cake. Only one person had challenge understanding the
+orientation of the RGB LED, and another misplaced the speaker by 90
+degrees. Other than that, no problem at all.
+
+And as always, now the job is almost done for everyone but me. We ran five
+rounds on the hotplate. Twice I forgot to monitor temperature and let it
+go too low, but luckily nothing went wrong. About 2 or 3 boards needed
+manual fixups, like un-shorting resistor packs, but after that every
+single one works. All the LEDs light up, too.
+
+One interesting problem: on one board the sound is intermittent. I pressed
+down the MCU, which seems to solve the issue for once and for all.
+
+We ended at around 21:40, but then one person came back telling me their
+battery holder broke. The positive contact snapped off. The only way is to
+replace it, so that's what I did.
+
+One remarkable observation: despite the facts that
+
+- my uni is mostly boys
+- my major is mostly boys
+- TechJI is mostly boys
+
+this workshop has more girls. This is really nice, because I experienced
+a level of diversity not present in a room of dudes. This is the future we
+want.
+
+At the end we took out more trash than we ever imagined:
+
+![A box full of waste: wipes, packaging, bubble wrap,
+etc](img/reflow-workshop/trash.jpg)
+
+## 2023-05-15, Monday
+
+This morning I moved the tables and chairs back where they belong, and put
+away every tool that I used. It's been exactly two months since this
+journal began, and it's finally coming to an end. I believe it's time for
+some reflections like I always do post-workshop.
+
+### What did I do right?
+
+- I began early (or, in other words, I prepared for way too long)
+- I bought most things with redundancy in mind
+- I designed a poster
+- I did it myself and proved it was possible
+- I showed my friends how to do it and now they can help me
+- I felt confident throughout the workshop
+
+### Who should I be thankful for?
+
+- Whoever funded this hackerspace and the ones who run it
+- My friends at TechJI for the help
+- My parents for their money
+- Kliment for the idea, design files, and help
+- Every delivery person who sent my packages
+
+### How did this workshop help me mature?
+
+- I fulfilled a dream since 2019
+- I learned to communicate with corporate sales, and to be shameless
+ around them
+- I even made one (1) phone call, even though it didn't help
+- I have killed my fear of reflow soldering
+
+### What can be improved?
+
+- When I said I ordered things with redundancy in mind, that does not
+ include the LEDs
+- I FORGOR MY BAG 💀
+- Sunday session went overtime due to ORS (Obsessive Reworking Syndrome)
+- Should have used email to organize a bunch of people I don't know
+- Should not have trusted WeChat for any purpose
+
+People have expressed wishes for more sessions in the future. Indeed, if
+I were rejected by someone else, I'd be sad. Sadly this is not likely,
+because (1) we've run out of chips, and (2) it is an extremely
+time-consuming workshop (only next to keyboards). More people = more
+inspections = more hotplate runs = more manual fixes. I would very much
+like to take a break from workshops.
+
+## 2023-05-16, Tuesday
+
+Today I took care of the accounting required for reimbursement. The sum
+worked out to be 1380.80 RMB (but I definitely spent more than that since
+I didn't ask for an invoice for everything).
+
+3 invoices are on paper, 8 are electronic. One vendor sent me an invoice
+in .ofd format (which is some kind of Chinese alternative for PDF, namely
+GB/T 33190-2016). The format is poorly supported except for a handful of
+commercial readers. It took me ages to find an online service and convert
+it to PDF.
+
+I sent all of them to the supervisor and I hope he gets back to me soon.
+
+My old keychain (which is a keyring on a green binder clip) was lost
+today, but just at the right time: introducing the Kitten PCB keychain!
+
+![PCB and two keys on a keyring](img/reflow-workshop/keychain.jpg)
+
+(Fun fact: in the workshop I told everyone "even if you fail, your failed
+PCB could make a nice keychain." Glad it never happened.)
+
+## 2023-05-22, Monday
+
+Supervisor says he "lost" one of the invoices due to the "gust of wind"
+that blew it away while he was away from office. And I was like …OK?
+
+Not that I'm mad. I cannot reject the null hypothesis that he is right.
+I'm just upset that it's the one that carries the most value. 417 RMB out
+of 1380 is 30%.
+
+So I asked JDB and they said they can't just print another one. Instead,
+all they can do is make a photocopy from their archive, then stamp it.
+
+I'll wait.
+
+## 2023-06-01, Thursday
+
+After weeks of waiting, I ask JDB what they've been up to, only to find
+that the sales rep I talked to has resigned and now I'm talking to another
+person. They gave me another number, who agreed to mail me the thing.
+
+## 2023-06-06, Tuesday
+
+The invoice copy is here. I'm gonna hand it to the supervisor tomorrow.
+
+## 2023-06-07, Wednesday
+
+Supervisor now has the invoice.
+
+Later he sent me a spreadsheet because the accounting office needs me to
+fill in an inventory of what I bought/used, how many, and when. I, being
+the sole accountant of the workshop, had no trouble doing it. But I don't
+like the attitude of theirs at the accounting office. This is a great way
+to ensure I won't hold a workshop here again.
+
+To get revenge, I did a passive-aggressive, highly accurate inventory that
+ended up 45 rows long, detailing every single purchase and consumption
+(even only one) on the invoices. I also left this note, in white font, on
+Sheet 2:
+
+> do you know how much work i put into this? of course you don’t. and now
+> you try to challenge me to count my consumables? well let me tell you
+> something. i HOSTED the workshop. of course i know how many batteries
+> i used. OF COURSE i know when i ordered them, when i received them, and
+> when i unboxed them. stop trying to refuse me my money. i am invincible.
diff --git a/docs/projects/rickstodon.md b/docs/projects/rickstodon.md
index b5c9a7d..5ee44d0 100644
--- a/docs/projects/rickstodon.md
+++ b/docs/projects/rickstodon.md
@@ -1,4 +1,8 @@
-# Rickstodon - Rickroll Your Friends (or Foes) With A Fake Mastodon Registration Page
+# Rickstodon
+
+Probably earlier than 2020-04-23
+
+Rickroll Your Friends (or Foes) With A Fake Mastodon Registration Page
## -- Introducing a new way to present an old prank
diff --git a/docs/projects/sirtet.md b/docs/projects/sirtet.md
new file mode 100644
index 0000000..7003c78
--- /dev/null
+++ b/docs/projects/sirtet.md
@@ -0,0 +1,248 @@
+# SIRTET
+
+2022-06-17
+
+I saw my mother play a mobile game the other day. She invited me to play
+along. It was one of these Tetris knockoffs where you drag one of the
+three given tetromino-like pieces (although there are many more, some
+aren't even continuous) onto an 8x8 map without overlapping, and each time
+you fill a row or column it clears up with a pleasant explosion effect.
+When you manage to place all three, they give you three more. The game is
+over once none of your pieces can be placed. This is fun and all, but
+there were ads after each game. I began to ponder: Can I make it in C?
+
+Of course I can. The map is just an 8x8 array (but I wish to make the size
+adjustable), and the pieces are just smaller arrays with some metadata.
+This just sounds like another CS101 assignment. I just need to be really
+careful with pointers and better yet, double pointers.
+
+## First prototype
+
+The initial design is that the map is an array of pointers representing
+rows, which point to arrays representing blocks of each row. Each block is
+a `char`, which is a space (`0x20`) for vacant blocks and a plus sign
+(`0x2b`) otherwise. Both the `char`s and `char*`s were manually malloc'd
+so as to avoid variable length arrays (VLA).
+
+As to the pieces, I defined `struct piece` with three attributes:
+
+- Height `int h`
+- Width `int w`
+- `char* blocks`
+
+The length of `blocks` is `h * w + 1` including the null terminator, and
+represent the flattened shape of the piece. Examples are:
+
+- `+` ← a single block
+- `++` ← 1x2 or 2x1, depending on `h` and `w`
+- `++++ ` ← a 2x3 L shape (see below)
+
+(For consistency, when I say `m x n`, I mean `m` rows by `n` columns.)
+
+I came up with 17 shapes, but a problem arises. If you need a slim
+L shape, there are four directions you can rotate it into; moreover, you
+can transpose each of them, making it eight:
+
+```
+++ ++ + +
++ + + +
++ + ++ ++
+
++++ + +++ +
++ +++ + +++
+```
+
+We can either define all the rotated and transposed versions for these 17
+shapes, or implement some rotation and transposition functions, which
+turned out not difficult to write. They basically did these four things:
+
+- Take a `struct piece*`, denoted `pc`
+- Make a backup of `pc->blocks` in local variable `old`
+- Carry out the rotation/transposition from `old` to `pc->blocks`
+- Swap `pc->h` and `pc->w`
+
+When handing out a piece to the player I just need to
+
+- Draw a random piece from the pool of 17
+- Rotate it 0 to 3 times
+- Transpose it 0 or 1 time
+- Put its pointer somewhere in the player's array of `struct piece*`s
+
+It turns out, since the pool of 17 should be (and is) immutable, I have to
+make a copy before I do anything to them. It is malloc'd and thus has to
+be free'd when it is placed on the map.
+
+The rest of the code is just `if`s and `for` loops. Within a few hours
+I was able to throw together a stdin-stdout version of the game, which
+I named SIRTET (its relationship to Tetris is left to the reader to
+decide). Here is a demo of the first two steps of the game:
+
+```
+$ ./sirtet
+ --------
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+| |
+ --------
+Piece 0
+++
+++
+
+Piece 1
+ +
+++++
+
+Piece 2
+++
+++
+++
+
+Input [piece #] [row #] [col #]: 0 0 0
+ --------
+|++ |
+|++ |
+| |
+| |
+| |
+| |
+| |
+| |
+ --------
+Piece 1
+ +
+++++
+
+Piece 2
+++
+++
+++
+
+Input [piece #] [row #] [col #]: 2 2 0
+ --------
+|++ |
+|++ |
+|++ |
+|++ |
+|++ |
+| |
+| |
+| |
+ --------
+Piece 1
+ +
+++++
+
+Input [piece #] [row #] [col #]:
+
+```
+
+And guess what? Zero memory leak! Check out this beauty:
+
+```
+$ valgrind --leak-check=full --show-leak-kinds=all ./sirtet
+==46064== Memcheck, a memory error detector
+==46064== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
+==46064== Using Valgrind-3.19.0 and LibVEX; rerun with -h for copyright info
+==46064== Command: ./sirtet
+==46064==
+ --------
+| |
+ ....
+| |
+ --------
+Piece 0
++++
++++
++++
+
+...
+
+Input [piece #] [row #] [col #]: 0 0 0
+
+[15 steps later]
+
+Game over
+==46064==
+==46064== HEAP SUMMARY:
+==46064== in use at exit: 0 bytes in 0 blocks
+==46064== total heap usage: 115 allocs, 115 frees, 3,069 bytes allocated
+==46064==
+==46064== All heap blocks were freed -- no leaks are possible
+==46064==
+==46064== For lists of detected and suppressed errors, rerun with: -s
+==46064== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
+```
+
+I know it's a tiny accomplishment, but it made my day.
+
+Now, a few problems. This `Input [piece #] [row #] [col #]: ` prompt isn't
+super friendly, and ideally I should be able to control everything with
+a consistent keybinding. And having a map that appears twice as high as
+it's wide sure isn't pretty. That's when I thought of ncurses.
+
+## ncurses
+
+I've always wanted to use ncurses somewhere. My CS professor promised
+a bonus if I had used ncurses for a course project called One Card, an Uno
+knockoff (why are all my projects knockoffs?), but I was struggling to
+stop it from segfaulting. Now that I've tackled those, I can finally try
+it out. I got my tutorial at [tldp](https://tldp.org/HOWTO/NCURSES-Programming-HOWTO/).
+
+It turns out pretty straightforward. Instead of `printf`ing, you just `mvprintw`
+(where `mv` stands for move and `w` stands for window) by supplying a pair of
+coordinates; the same goes for other functions. To draw a solid block at `(y,
+x)`, all I had to do is print two inverted spaces:
+
+```
+mvaddch(y, x, (' ' | A_REVERSE));
+addch(' ' | A_REVERSE);
+```
+
+The second line did not need a `mv` because the `mvaddch` above
+automatically moves the `x` coordinate to the right by one. To draw
+a rectangle, I just put `ACS_HLINE`, `ACS_VLINE`, `ACS_ULCORNER` etc. at
+all the right places. ncurses provided macros and functions for colors
+too.
+
+Soon, I was able to draw the map, the piece hovering above to be placed,
+and the rest of the pieces.
+
+I also managed to react to the player's keystrokes with `getch()`.
+I defined three sets of navigation keys: arrow keys, wasd, and of course
+the vim user loyalty, hjkl. The player may also press `[` and `]` to
+switch pieces. There were a lot of switch-cases and bound checking.
+
+Finally I used `getopt.h` for the first time to parse CLI arguments, aka
+`argv`. The concept of storing argument values in global variables
+honestly surprised me, but having done some AVR, I got over it very soon.
+You can customize the map size (both height and width independently), and
+the maximum number of pieces you have at once. My editor plugin warns me
+about insecurities in `sscanf`, but I disregarded them.
+
+I tried valgrind on the ncurses version, but the results were monstrous.
+It was later revealed to me that ncurses does a lot of memory management
+itself and inevitably it will confuse valgrind. ncurses does provide
+a workaround to debug memory usage. Anyway, at this point there is no
+longer any point. I proceeded to add a few fancy things like game stats
+and called this project complete.
+
+## Demo
+
+<video controls><source src="../img/sirtet/demo.mp4"></video>
+
+MPEG-4 Video (252 KiB)
+
+## Room for improvement
+
+This whole project was made with a premise in mind that no one else would
+play it. If anyone else were to play it, I would have made a few
+adjustments:
+
+- `--no-color` option, i.e. use monochrome indicators other than red/green
+- Highscore
+- `--help` option
diff --git a/docs/random/2024-04-05.md b/docs/random/2024-04-05.md
new file mode 100644
index 0000000..fe62e55
--- /dev/null
+++ b/docs/random/2024-04-05.md
@@ -0,0 +1,407 @@
+# 2024-04-05
+
+Written 2024-04-06
+
+Yesterday I:
+
+- Bought a bicycle
+- Attended a fairy tale discussion
+- Went to an iDKHOW concert
+
+Each of these deserves its permanent record so I'm writing it right here.
+
+## Bicycle Deal
+
+### Craigslist
+
+In March I was tempted to buy a bicycle. Reasons:
+
+- My 373 project is related to cycling
+- I'm bored of walking all the time
+- Cycling is an excuse of not going to the gym
+
+I tried my roommate's bicycle earlier; it was rusty and the tires were
+flat. Honestly I have no idea how strong his legs are if he actually
+managed to propel this megatron of a bike. I asked him where he got it,
+and he said craigslist. He met the seller outside Panda Express, north
+campus, and paid $85.
+
+There were three bikes on craigslist I was interested in: a mountain bike,
+a road bike, and a cruiser. The road bike is slightly cheaper, but the
+wheels seem too narrow to handle any adverse weather.
+
+I reached out to Ed (W8EMV) on fedi for advice. He recommended mountain
+bikes and told me about a bike repair co-op.
+
+So on 2024-03-20, I shot an email to the seller of the mountain bike. It
+was a Schwinn listed at $95. He responded the next day.
+
+(Shoutout to craigslist for not requiring an account. The entire
+conversation was done via email.)
+
+I told him I don't have a car so I prefer to meet somewhere walkable from
+me. We agreed on Kroger. We originally had planned to meet at the weekend
+(2024-03-30 and -31) but my landlord decided the immediate Monday
+(2024-04-01) was a good time to rip off the carpet and install some kind
+of vinyl floor. Besides, I bought a U-lock on ebay and it's not here till
+Monday.
+
+We postponed the trade to 2024-04-05.
+
+### The trade
+
+I showed up 20 minutes too early, so I entered Kroger to buy a small
+amount of groceries. The seller, Chris, arrived at 09:15 and brought me to
+his car, from which he unmounted the bike.
+
+Chris drove here from Grand Rapids, and is on his way to Detroit to see
+something related to the Detroit Tigers. I told him I was going to Detroit
+the same evening as well, to St. Andrew's Hall.
+
+He bought the bike for his son originally, but he never really rode it.
+"Bad for him, good for me," I said.
+
+I heard dogs barking. He explained that it was his two dogs in carriers.
+
+I asked him if he trusted me to give the bike a test ride around the
+parking lot. He said yes, so that's what I did. He just sat back in his
+car.
+
+The bike was like the smoothest thing I've ever rode. The brakes work
+perfectly. The tires were full, there was almost no defect, functional or
+cosmetic. Say no more, I'm taking this baby.
+
+I opened my wallet and counted 90 dollars. He stopped me right there. He
+said, "It's good. Go buy yourself something to eat."
+
+And that is how I got an almost new bike for $90.
+
+### The lock
+
+After I got the bike, the plan was:
+
+- Ride directly to north campus
+- Lock bike to campus bike rack
+- Attend lab
+- Ride home and lock to apartment bike rack
+
+But the lock was delayed. It _was_ in Detroit; then somehow it went to
+Alabama. I had to ride home and carry it to my bedroom, before walking to
+the lab.
+
+## Fairy tale discussion
+
+The topic was Romanticism. I wasn't a fan of this topic because it's not
+super relatable.
+
+Romanticism was born as a critical response to the Enlightenment, or the
+age of reason. Contrary to the science and rationality of Enlightenment,
+Romanticism focused on the spiritual: human emotions, especially the
+negative ones, called the "Nachtseite" (night side).
+
+We discussed the Byronic hero, who is sick of the rational and feels
+wanderlust, melancholy, misanthropy, and alienation.
+
+We were asked to come up with examples in popular media. Mine was Jesus of
+Suburbia, the protagonist of Green Day's _American Idiot_ (2004) who
+escapes the suburbs to seek a new life in the city. Eventually, he finds
+nothing and comes back home. Notable examples of Romantic emotion:
+
+- "American Idiot": misanthropy. He is disgusted by a society "controlled
+ by the media". Also the word "alienation" is literally in the chorus.
+- "Jesus of Suburbia": wanderlust. The whole song the titular character
+ curses society for its faults, which builds up to his final decision to
+ leave home to find what he believes.
+- "Are We the Waiting" and "Whatsername": melancholy. Just… listen to it.
+
+My professor, Laura Okkema, relates to the Byronic hero, from her
+experience of being alienated in American family gatherings. When I heard
+it I just treated it as a light-hearted story. Big mistake; read on.
+
+## iDKHOW
+
+### Timeline
+
+- 15:50 - I board the D2A2 bus at Blake Transit Center, Ann Arbor
+- 16:00 - The bus departs
+- 16:10 - I saw some cows
+- 16:50 - The bus arrives at Grand Circus Park, Detroit
+- 17:15 - I queue up in a parking lot behind St. Andrew's Hall*
+- 19:20 - I finally get into the venue
+- 20:00 - The opening act [Farhall](https://farhallmusic.com/) performs
+- 21:00 - iDKHOW performs
+- 22:30 - Show ends
+- 22:55 - I board the bus back to AA
+- 23:05 - The bus departs
+- 23:55 - The bus arrives at BTC
+- 00:?? - I board the Bursley-Baits bus from CCTC to Bursley
+- 00:30 - I come home
+
+\* Note: the venue is technically The Shelter, part of SAH
+
+### Waiting outside
+
+From 17:15 to 19:20, I waited outside. There were 20 or so people in the
+line. A bunch of Detroit Tigers fans were vibing in a big tent nearby, in
+a parking lot. The DJ put on some really loud hip hop.
+
+I like hip hop. But the TR-808 was nonstop, and the synth bass was
+indistinguishable from seismic waves. If you know me, I like dynamics, and
+loud music for two hours is not dynamic. I hope they're having fun because
+I sure am not.
+
+I scanned the crowd. There were around 100 people, and probably one third
+of them wore some sort of merch. A handful had VIP badges, and stood in
+a different lane. I'm cool with that.
+
+At one random moment Dallon (iDKHOW's only permanent member rn) walked out
+of nowhere and the crowd cheered.
+
+A guy stood behind me. His name is Andy and is from MSU, a graduate
+student in physics, doing nucleus stuff. He discovered the band in the
+pandemic and just randomly found out they were touring last month, so he
+bought a ticket. I wish I could afford resale tickets that's like 5x the
+face value.
+
+He's totally chill, just having a casual Friday evening. He's not like
+other fans. He did not know what the band's prior album is called. And
+that's ok; I hate gatekeeping.
+
+I forgot to have my ticket ready at the entrance, so I wasted five minutes
+and ~20 people got ahead of me. As a consequence, I lost Andy and was
+forced to spend the rest of the show alone.
+
+### Waiting inside
+
+Inside the venue a merch stall sold overpriced T-shirts and jackets.
+I didn't bust my wallet. I headed to the restroom. The urinator was a wide
+tub of ice (?) and apparently you can piss in it???
+
+So anyway I joined the pit. It was dim af but there was something worse.
+
+Music.
+
+Music was blasting through all the loudspeakers. It was again nonstop,
+crossfading into one another. I absolutely hate the vibes. The worst part
+is the song that sampled a revolver. It goes like:
+
+> *barrel roll* bang bang bang
+> *barrel roll* bang bang bang
+
+On the other hand, one could argue that the song is, quite literally,
+fire.
+
+If I made music, the worst thing you could do with it is play it before
+a show. I've never heard of it. It's not live. I did not consent to it.
+Good songs become mediocre and average songs become shit.
+
+I put on my earplugs.
+
+### Farhall
+
+The Benches, who were supposed to open for iDKHOW, were sick so Farhall
+came last minute. Their voice is beautiful but unfortunately I was unable
+to make out the lyrics. Genre-wise, it's indie pop.
+
+### iDKHOW
+
+After another forty minutes of miserable waiting, iDKHOW's touring members
+came up stage followed by Dallon. They opened with a rather interesting
+choice: "SPKOTHDVL" (speak of the devil), not a lead single. The first
+line is "Your front page boy is finally here". Yeah Dallon sure you are.
+
+The new record is titled _GLOOM DIVISION_ and there were explicit songs,
+topic or lyrics. It was the first time he sang "fuck" and "bitch", and one
+song was literally about sex. About three times at the show he advised the
+audience below 25 to "cover your ears". lmao I'm wearing earplugs.
+
+Little by little I pulled out the earplugs because I can't hear the crowd
+and it blocked high frequencies. I removed them during my favorite songs,
+and put them halfway back in during others.
+
+My favorite song played live is ["A
+Letter"](https://thebrobecks.bandcamp.com/track/a-letter), written before
+iDKHOW's formation, when Dallon was in the band The Brobecks. He said it
+was the band's "least popular song" if you look at the numbers. But he
+added it was the best. I agree.
+
+He split the crowd in half and instructed both sides to harmonize. He said
+it was like church. It was indeed majestic.
+
+In the middle of "Visitation of the Ghost", also a Brobecks song, he split
+an aisle and walked down the middle while the crowd repeated the line "oh
+my lord, lord lord lord". I'm an atheist so the one I was referring to was
+actually the New Zealand singer.
+
+Generally, the crowd knew the lyrics to older songs better, and I'm guilty
+of that.
+
+Dallon casually admitted to sampling a car horn in the ending track,
+"iDIOTS OF Oz". We have achieved peak [xkcd #780](https://xkcd.com/780/).
+
+Over the duration of the show, Dallon threw two picks, but neither was in
+my range.
+
+### The cringeworthy story of my PCBs
+
+Warning: this story is cringe. If we ever meet in person, even if we're
+friends, you are _not allowed_ to bring up this story.
+
+<details markdown="1">
+<summary>You have been warned</summary>
+
+Last month I trained myself to use the Bantam CNC. I milled [9 iDKHOW
+logos on a PCB](../projects/idkhow-pcb.md) and announced on Reddit that
+I would be happy to give them out.
+
+Only one person replied they would be coming. I told them my clothing and
+location, but they were late. So I just stood there surrounded by
+strangers, snatching the ziploc in my hand, hoping someone asks me what
+they are.
+
+Do you identify my fatal mistake? Yes — PCBs are a novel souvenir and I am
+not socially competent enough to just hand them out to strangers. My nerd
+brain had assumed that everyone would be dazzled by the beauty of copper,
+but everyone was just minding their own business.
+
+The problem was the fucking hip hop DJ. If there were no loud music,
+I could have made a conversation in a break of silence. But in retrospect,
+there is no evidence that I could pull that off.
+
+The real issue is they came in pairs, even groups, and they're constantly
+talking. I could not join their clique, or it wouldn't be a clique.
+
+I tried Andy, who also came alone. He wasn't interested, and it would be
+offensive to force anyone to take it. So I put them back in my pocket.
+I was like sure, why not hand it out inside? It should be quiet there.
+
+NOPE
+
+I tried a guy next to me. I said "hey man, may I interest you with some
+souvenir?" And he was like "no I'm good".
+
+At this very moment I have fucked up. I sounded like a shady tour guide.
+There is no convenient way in the English language to express a lack of
+profit incentive, without building implicit trust with the person. That
+takes time and conversation, but again the loud music forbids it. So fuck
+you, music-through-the-loudspeaker-blaster.
+
+After the opening act was done, a dad who was the guardian for his
+daughter approached me to ask me whether I enjoyed it. I was compelled to
+say yes. And as if it wasn't awkward enough, I offered my PCB.
+
+I said, "Oh that reminds me, you can have one of these. I made them
+myself."
+
+He declined without even looking at them. And that was the correct choice.
+
+I was a stranger. And the default option, when meeting a stranger, is to
+not accept anything.
+
+Hey, can you bring back the revolver song? Because I want to die.
+
+All I wanted, at that point in time, is to get rid of the ziploc. Even
+flushing it in the toilet would bring me relief. But copper and fiberglass
+do not belong in residential sewage.
+
+Basically I tried to drag myself as far away from the two people I talked
+to as possible, but it was so crowded I could hardly move. So I was pinned
+in this awkward location the entire show.
+
+It was distilled alienation.
+
+I admitted defeat and carried the ziploc back home. Every time I lay my
+eyes on it, it incites violent thoughts.
+
+It's the Nachtseite.
+</details>
+
+### The Nachtseite
+
+On my way home, I sent this email to my professor.
+
+>Hi Laura,
+>
+>I'm back from the Detroit show. This is the first live music event I ever
+>attended outside campus. The one and a half hour where they actually
+>performed was fire, so you can be assured I'm ok.
+>
+>But the four hours of waiting was traumatic. I was one of the few who
+>came alone. I had imagined music would "bring us all together", but they
+>had their own cliques and there was no humanly possible way to tap into
+>a conversation without coming off as cringe. I feel alienated.
+>
+>The venue tortured my ears with music. Not bad, just unnecessarily loud.
+>At one point I wished I could trace back to the studio to physically
+>slash the drumhead. I feel misanthropy.
+>
+>At first I pretended to have a good time, but soon I stopped bothering.
+>Bored (the irony is that their closing track is called "Boring") and
+>wishing to teleport somewhere else. I feel melancholy *and* wanderlust.
+>
+>Clearly I've felt the Nachtseite (again ironically, they played a song
+>called "Sunnyside"). It seems like I'm the Byronic romanticist hero, but
+>as the show began, I returned to rationality because I know what is going
+>on. Emotionally I'm not attached to the band and their early songs, and
+>it feels like I'm the Clara in the room.
+>
+>Perhaps whether someone is a romanticist or enlightenment-ist can change
+>depending on circumstance? Or, one could be both, at the same moment?
+>
+>also I'm having tinnitus.
+>
+>Yours,
+>Frederick Yin
+
+Note: Clara is a character in ETA Hoffmann's "The Sandman". She is always
+rational in the story.
+
+This email was composed partly on the bus, and partly at home.
+
+### Trip back home
+
+The hip hop was still on when my show ended. I took a detour on Broadway
+but still made it on time. At Grand Circus Park, an old man asked me if
+I knew any ATMs nearby as he didn't have a phone. I was cautious of my
+possessions as I took out my phone to look at the maps. So that's what
+it's like to be approached by a stranger. I fully realized the cringe of
+my actions earlier, and I understand why they reacted this way.
+
+I told him there's a PNC ATM at Broadway + John R.
+
+Once everyone was on the bus, the bus driver warned us not to use the
+bathroom, despite his best effort to disinfect it. I do not wish to see
+what's inside.
+
+## The dream
+
+At home I took a shower and went to bed.
+
+I dreamed of riding my new bicycle all around the town. I shifted gears at
+will. It was… surprisingly real, although I seemed to have remembered the
+numbers backwards. There was an uphill road where I had to get off and
+push the bike. But for the rest of my aimless journey, I rode as fast as
+the wind.
+
+I woke up as though I had rode perhaps 10 kilometers. But in reality, I've
+rode less than two. Is this wanderlust?
+
+## Conclusion
+
+- Thanks Chris for the trust and saving me 5 dollars
+- Fuck you USPS for sending my lock to Alabama
+- Can't believe how long I waited
+- Constant loud music is torture
+- Please stop playing songs with gunshots
+- Waiting for a show alone is peak alienation
+- Thanks Andy for making this slightly better
+- Sorry Laura I know what it feels like to be the Byronic hero now
+- Don't devote time on things nobody asked for
+- Earplugs are useful
+- I've crossed out another one of my [life goals](life_goals.md), despite
+ great pains
+- I will probably never go to an iDKHOW show ever again
+- I hope you found the ATM, random person
+- I want to ride my bicycle I want to ride my bike
+- I want to ride my bicycle I want to ride it where I like
+- Oh the lock is here?
diff --git a/docs/random/2bugs1day.md b/docs/random/2bugs1day.md
new file mode 100644
index 0000000..360472c
--- /dev/null
+++ b/docs/random/2bugs1day.md
@@ -0,0 +1,117 @@
+# 2 Bugs 1 Day
+
+2023-11-23
+
+If I had a nickel every time I encountered a cursed bug today, I'd have
+two. It's not a lot but weird how it happened twice.
+
+## Bug 1
+
+My roommate asked me about a bug in his C code. He passed a value to
+a function. The value is not supposed to be zero, but in the output, it
+apparently is. Why?
+
+We could not find any lines of code that could have changed this value. It
+was initialized with a command line argument (i.e. `atoi(argv[2])`), then
+remains read-only.
+
+After ruling out off-by-one errors and floating point rounding,
+I hypothesize it has something to do with the data structure. The value is
+within a struct as follows:
+
+```c
+struct foo_t {
+ bar_t bar[256];
+ int value;
+} foo;
+```
+
+`foo` is a global instance of `struct foo_t`. To initialize it, we set
+`value` to a command line argument and initialize `bar` with a for loop,
+which looks like:
+
+```c
+for (int i = 0; i < arg1; i++) {
+ for (int j = 0; j < arg2; j++) {
+ foo.bar[i * arg2 * arg3 + j] = 0;
+ }
+}
+```
+
+I realized that `arg3` was unnecessary. By multiplying it, we're spacing
+out the indices too much. If `arg1` gets big enough, we'll get a buffer
+overflow.
+
+It just so happens that all arguments are powers of 2, and `bar` is an
+array of 256. Which means… You'll overwrite `value`!
+
+We removed `arg3` and sure enough, it worked.
+
+Conclusion: Lack of bound checking in C.
+
+## Bug 2
+
+Right after we hunted down this bug, my roommate said there's another bug
+in VSCode that has bothered him for a while. It seems like whenever he
+types the word "store" in a Markdown code block, the syntax highlighting
+(regardless of language) breaks. He looked through the VSCode repo and
+didn't find anything particular.
+
+We found that the keyword doesn't have to be "store", it just needs to be
+"re" + whitespace.
+
+![Some C code in a Markdown code block, containing a string literal "re ".
+Highlighter refuses to match curly braces surrounding it, and completely
+stops working below.](img/2bugs1day/re.png)
+
+What does `re` stand for? The Python package?
+
+This is so cursed that I decided to make the ultimate sacrifice — to
+install VSCode on my own machine.
+
+However, I failed to reproduce. My roommate correctly suggests it's
+a problem with a plugin. He disabled all the Markdown-related plugins on
+his machine. The bug is still there.
+
+He then disabled all plugins (except those bundled). The bug is gone.
+
+VSCode has a "plugin bisect" tool that does a binary search to find the
+problematic plugin in O(log(# plugins)) time. I did that and the problem
+is…
+
+OCaml Platform.
+
+Do you realize how shocked we were? Like, we would rather believe it was
+ghosts or something. OCaml is literally the single most innocent plugin.
+
+We looked at OCaml Platform's code and found something interesting.
+
+```json
+{
+ "repository": {
+ "reason-code-block": {
+ "begin": "(re|reason|reasonml)(\\s+[^`~]*)?$",
+ "end": "(^|\\G)(?=\\s*[`~]{3,}\\s*$)",
+ ...
+ },
+ ...
+}
+```
+
+I do not know how this works, but boy I sure know regex checks out. We
+tried `reason`, and yes, it broke as well.
+
+This code was added in a 2020 commit, and remains unchanged since. It's
+such a minor bug that I believe 90% its users didn't even notice.
+I encouraged my roommate to file a bug report — or even a pull request.
+
+My hypothesis is OCaml programmers simply don't program in anything else.
+
+Conclusion: OCaml plugin ships a regex with false positives.
+
+## Takeaway
+
+1. There is a reason for every bug.
+2. There is not an obvious reason for every bug.
+3. C will just silently overflow your buffer without you knowing. Can't
+ wait till Linux is 100% Rust
diff --git a/docs/random/ergo_m575.md b/docs/random/ergo_m575.md
new file mode 100644
index 0000000..097c790
--- /dev/null
+++ b/docs/random/ergo_m575.md
@@ -0,0 +1,88 @@
+# I'm Using a Trackball (Logitech ERGO M575)
+
+2022-06-24
+
+I've always heard stories about people worshipping the trackball for e.g.
+comfort, precision, curing their carpal tunnel, etc., but I've never used
+one… until now.
+
+Note: this blogpost has no insight whatsoever. It's just me talking random
+stuff about my new trackball.
+
+![Top view of the trackball. It is asymmetric. A shiny blue trackball (3-4
+cm in diameter) is on the left. The chassis on the right is made of black
+plastic, has a "logi" logo, and features two mouse buttons and
+a scrollwheel. At the tip of the left button there are two protruding
+buttons in a vertical layout.](img/ergo_m575/top.jpg)
+
+Joke's on me, the first thing I did after putting it on my desk is to
+instinctively attempt to drag it around. Obviously the rubber pads on the
+bottom stopped me from doing that.
+
+![Left view. The trackball fits inside a socket.](img/ergo_m575/left.jpg)
+
+![Right view. There is a groove along the bottom for the side of your
+palm.](img/ergo_m575/right.jpg)
+
+![Bottom view. A mostly flat surface except for an engraved "ERGO M575",
+a few rubber pads, an on/off toggle switch, a dongle/bluetooth switch
+button, a hole exposing bottom of trackball, and the lid of the battery
+holder.](img/ergo_m575/bottom.jpg)
+
+The hole on the bottom is large enough for me to poke my pinky through and
+push out the trackball.
+
+![Left view after taking out the trackball. The ball leans against the
+chassis and looks glittery.](img/ergo_m575/trackball.jpg)
+
+It turns out, like a regular optic mouse, it's also laser powered.
+
+![Closeup of the empty socket. There is a laser transceiver deep in
+there.](img/ergo_m575/socket.jpg)
+
+The three tiny white dots you see are rollers that support the trackball
+and maintain friction. Allegedly, these were made of steel in its
+predecessor, M570, but now they are plastic.
+
+I've been using this for a dozen hours now, including web browsing, file
+managing, text editing (in LibreOffice and vim), and CAD. Here's what
+I feel about it in general.
+
+## Pros
+
+- It's beefier than my old ThinkPad mouse and fits my hand better
+- Trackballs are cool
+- The Logitech® Unifying™ USB® receiver works out-of-the-box on Linux®
+- The motion is smooth and detection is precise (but I have no proof that
+ it is more precise than my regular mouse)
+- The ball never slips my thumb
+- It saves desk space
+- It's taller and thus less likely to get buried in piles of paper
+- I no longer have to worry about pressing forward/back by mistake when
+ clicking the middle button
+
+## Cons
+
+- The right mouse button is too sensitive and sometimes depresses under
+ the weight of my hand
+- The left and right buttons on the scrollwheel are now two discrete,
+ vertical buttons without context
+- I just found if you hit these buttons with the tip of your finger real
+ quick, you can trigger them electronically but not mechanically
+- It's unfriendly to left-handed people
+- It's harder to orient my hand immediately upon contact
+- Hence, it's harder to switch my right hand from keyboard to mouse
+- Hence hence, I'm more reluctant to do the reverse which puts more burden
+ on my left hand
+- The trackball feels kinda like a touchscreen you swipe on with your
+ thumb. I was urgently producing some SVGs one night when I realized my
+ actuation was less precise than before. Luckily I finished them before
+ any lasting fatigue. (2022-06-24 update: Guess what else to blame?
+ A piece of grime stuck on one of the rollers, now that I've taken out
+ the trackball to check. Now it's smoother but I still find it tiring
+ when used for hours.)
+
+## Conclusion
+
+There is no conclusion. I told you there is no insight. I just wanted to
+share some cool new hardware. Now go do something else.
diff --git a/docs/random/how_fkfd_is_made.md b/docs/random/how_fkfd_is_made.md
index 0615df9..fcb3803 100644
--- a/docs/random/how_fkfd_is_made.md
+++ b/docs/random/how_fkfd_is_made.md
@@ -2,26 +2,48 @@
2020-07-21
-I don't consider myself a cartoonist, let alone good cartoonist. I am unqualified at comics in all aspects, you know? I can hardly draw anything that's not a rectangle, circle, or straight line; for every final stroke you see on the canvas, I have undone and redone three times; my update frequency is like `abs(tan(x/k))` where k is around 10 days\^-1 [1]. One day in May I pushed 4 comics. And then I completely skipped June.
+2024-02-20: Fixed broken links in footnotes thanks to anonymous reader via
+email
-Let me keep it short and talk about how an fkfd comic is made. Not technical details (i.e. export as webp, then png, then run `./submit.py`...) but how I come up with ideas and express them in comics.
+I don't consider myself a cartoonist, let alone good cartoonist. I am
+unqualified at comics in all aspects, you know? I can hardly draw anything
+that's not a rectangle, circle, or straight line; for every final stroke
+you see on the canvas, I have undone and redone three times; my update
+frequency is like `abs(tan(x/k))` where k is around 10 days\^-1 [1]. One
+day in May I pushed 4 comics. And then I completely skipped June.
+
+Let me keep it short and talk about how an fkfd comic is made. Not
+technical details (i.e. export as webp, then png, then run
+`./submit.py`...) but how I come up with ideas and express them in comics.
## Life -> Idea
-However boring your life may be, you always find ideas bumping from inside your brain; they may just be a little dull. I catch my inspirations right away when I come up with one. Here is a list of common ideas.
+However boring your life may be, you always find ideas bumping from inside
+your brain; they may just be a little dull. I catch my inspirations right
+away when I come up with one. Here is a list of common ideas.
- I look at things that vaguely resemble others, and make fun of that[2].
-- I ask myself questions "what if we...?" and imagine the possibilities in the wildest way[3].
-- I contemplate about today's society and world, and when I cannot squueze jokes out of them, I make them look thought-provoking[4].
-- I voice concerns about today's technology and its relationship with human life[5].
-- Also comics that are straightforward xkcd parodies[6], or even ones that reuse xkcd graphics[7].
+- I ask myself questions "what if we...?" and imagine the possibilities in
+ the wildest way[3].
+- I contemplate about today's society and world, and when I cannot squueze
+ jokes out of them, I make them look thought-provoking[4].
+- I voice concerns about today's technology and its relationship with
+ human life[5].
+- Also comics that are straightforward xkcd parodies[6], or even ones that
+ reuse xkcd graphics[7].
- Giant buildup for giant fun, like flowcharts and tables[8].
- Irony pointed at one certain type of person[9].
- Inevitable graph jokes[10].
- Personal rant[11].
- Puns and wordplay[12].
-The ideas can be funny or serious, revolving around an outdated or emerging topic, whatever. Apply the craziest spices until the idea is hot enough to attract a handful of people. Avoid *really* niche hobbies, and definitely don't go to depths (i.e. inside jokes), for the sake of audience coverage. As a side note, I never deliberately check if my idea has been taken. Too much trouble. I do avoid copying ideas I know to exist, though.
+The ideas can be funny or serious, revolving around an outdated or
+emerging topic, whatever. Apply the craziest spices until the idea is hot
+enough to attract a handful of people. Avoid *really* niche hobbies, and
+definitely don't go to depths (i.e. inside jokes), for the sake of
+audience coverage. As a side note, I never deliberately check if my idea
+has been taken. Too much trouble. I do avoid copying ideas I know to
+exist, though.
## Others' Idea -> My Idea
@@ -34,19 +56,70 @@ My comics are large influenced by xkcd. A few other influencers:
## Idea -> Comic
-There are certain periods when I was super innovative and others super productive and/or artistic. The best scenario is when these overlapped, and I was able to commit my ideas on the canvas on the fly. That was definitely efficient, but later when I reflected on comics produced over this period, I would often sense creeping embarrassment for lack of due thoughts before submission, or regret over some imperfections like a forgotten punchline. The second best is those moments when I had a genuinely great idea (at least to me at that time), and had a notebook handy. Once I would jot down a few hint words, like "dead kids sent from heaven to haunt their anti-vaxx parents", but turns out I was terrible at remembering details. Soon I learned to draw a rough sketch, and continuously iterate, fixing grammatical problems and pruning unnecessary words (canvas space is precious). Occasionally, I do it on a whiteboard. Finally, I boot up my lappy, draw what's on the paper/whiteboard through my wacom tablet into krita, sometimes verbatim and sometimes with a few minor adjustments, then re-orchestrate the elements so they fit on a digital canvas. Behold! Digital "art".
+There are certain periods when I was super innovative and others super
+productive and/or artistic. The best scenario is when these overlapped,
+and I was able to commit my ideas on the canvas on the fly. That was
+definitely efficient, but later when I reflected on comics produced over
+this period, I would often sense creeping embarrassment for lack of due
+thoughts before submission, or regret over some imperfections like
+a forgotten punchline. The second best is those moments when I had
+a genuinely great idea (at least to me at that time), and had a notebook
+handy. Once I would jot down a few hint words, like "dead kids sent from
+heaven to haunt their anti-vaxx parents", but turns out I was terrible at
+remembering details. Soon I learned to draw a rough sketch, and
+continuously iterate, fixing grammatical problems and pruning unnecessary
+words (canvas space is precious). Occasionally, I do it on a whiteboard.
+Finally, I boot up my lappy, draw what's on the paper/whiteboard through
+my wacom tablet into krita, sometimes verbatim and sometimes with a few
+minor adjustments, then re-orchestrate the elements so they fit on
+a digital canvas. Behold! Digital "art".
## Footnotes
-[1] Which means my update speed peaks about once per month, while somewhere in the middle it's zero.
-[2] Notable examples: [Heroic Ball Pen](https://fkfd.me/13), [Chihuahuatamayo](https://fkfd.me/74), and [Keychane](https://fkfd.me/93).
-[3] Notable examples: [Night Plane Spotters](https://fkfd.me/70), [Soul Counter](https://fkfd.me/79), and [Amazon Ring](https://fkfd.me/87).
-[4] Notable examples: [Intolerance](https://fkfd.me/65), [Zeta-373](https://fkfd.me/77), and [Stray Cats](https://fkfd.me/90).
-[5] Notable examples: [Innovations](https://fkfd.me/73), [Intended Internet](https://fkfd.me/86), and [JavaScript](https://fkfd.me/101).
-[6] Notable examples: [Code Quality](https://fkfd.me/10), [Technology, Inc.](https://fkfd.me/11), and [Irrelevant xkcd](https://fkfd.me/67).
-[7] Notable examples: [Python](https://fkfd.me/6), [Entropy Harvesting Daemon](https://fkfd.me/81), and [Childhood Misconceptions](https://fkfd.me/92).
-[8] Notable examples: [Partitions](https://fkfd.me/16), [X-over-Y](https://fkfd.me/53), and [Python Code With No Documentation](https://fkfd.me/55).
-[9] Notable examples: [Flat Earthers](https://fkfd.me/21), [Blogging](https://fkfd.me/26), and [Wish](https://fkfd.me/71).
-[10] Notable examples: [Time Consumption](https://fkfd.me/9), [Turning Point](https://fkfd.me/59), and [Graph Nerd Sniped](https://fkfd.me/89).
-[11] Notable examples: [Smart Home](https://fkfd.me/46), [Middle Button](https://fkfd.me/52), and [Grades](https://fkfd.me/96).
-[12] Notable examples: [Wheeled Armchair and Armed Wheelchair](https://fkfd.me/54), [One-Letter Modifications](https://fkfd.me/69), and [Company Rules](https://fkfd.me/88).
+[1] Which means my update speed peaks about once per month, while
+somewhere in the middle it's zero.
+
+[2] Notable examples: [Heroic Ball Pen](https://fkfd.me/comics/13),
+[Chihuahuatamayo](https://fkfd.me/comics/74), and
+[Keychane](https://fkfd.me/comics/93).
+
+[3] Notable examples: [Night Plane Spotters](https://fkfd.me/comics/70),
+[Soul Counter](https://fkfd.me/comics/79), and
+[Amazon Ring](https://fkfd.me/comics/87).
+
+[4] Notable examples: [Intolerance](https://fkfd.me/comics/65),
+[Zeta-373](https://fkfd.me/comics/77), and
+[Stray Cats](https://fkfd.me/comics/90).
+
+[5] Notable examples: [Innovations](https://fkfd.me/comics/73),
+[Intended Internet](https://fkfd.me/comics/86), and
+[JavaScript](https://fkfd.me/comics/101).
+
+[6] Notable examples: [Code Quality](https://fkfd.me/comics/10),
+[Technology, Inc.](https://fkfd.me/comics/11), and
+[Irrelevant xkcd](https://fkfd.me/comics/67).
+
+[7] Notable examples: [Python](https://fkfd.me/comics/6),
+[Entropy Harvesting Daemon](https://fkfd.me/comics/81), and
+[Childhood Misconceptions](https://fkfd.me/comics/92).
+
+[8] Notable examples: [Partitions](https://fkfd.me/comics/16),
+[X-over-Y](https://fkfd.me/comics/53), and
+[Python Code With No Documentation](https://fkfd.me/comics/55).
+
+[9] Notable examples: [Flat Earthers](https://fkfd.me/comics/21),
+[Blogging](https://fkfd.me/comics/26), and
+[Wish](https://fkfd.me/comics/71).
+
+[10] Notable examples: [Time Consumption](https://fkfd.me/comics/9),
+[Turning Point](https://fkfd.me/comics/59), and
+[Graph Nerd Sniped](https://fkfd.me/comics/89).
+
+[11] Notable examples: [Smart Home](https://fkfd.me/comics/46),
+[Middle Button](https://fkfd.me/comics/52), and
+[Grades](https://fkfd.me/comics/96).
+
+[12] Notable examples:
+[Wheeled Armchair and Armed Wheelchair](https://fkfd.me/comics/54),
+[One-Letter Modifications](https://fkfd.me/comics/69), and
+[Company Rules](https://fkfd.me/comics/88).
diff --git a/docs/random/i_respect_furries.md b/docs/random/i_respect_furries.md
new file mode 100644
index 0000000..7026146
--- /dev/null
+++ b/docs/random/i_respect_furries.md
@@ -0,0 +1,38 @@
+# I Respect Furries
+
+2022-06-05
+
+I respect furries. By "respect", I mean I acknowledge and appreciate
+certain aspects of a furry's personality. As of 2022, I am not a furry.
+But I know a thing or two about them. Despite this single umbrella term,
+they are actually a diverse community. In this blogpost however, there
+seems to be no point in taxonomy.
+
+Warning: every sentence I write is speculation, i.e. not backed by any
+scientific claim or even empirical observation. If you are a furry reading
+this and what I wrote offends you, feel free to drop hatemail to my
+mailbox at any time.
+
+It takes a lot of effort to be a furry. A minimum of one fursuit is
+a must, which often costs a handful to acquire and maintain. The
+bourgeoisie have it easy, but the rest of them have to be frugal, while at
+the same time taking care of their fursuit. I respect that, as much as
+I respect aspiring musicians saving every penny for a new guitar.
+
+It might seem strange to see a fursuiter on their way to a convention, but
+what different is it from a fashion show? They both feature impractical
+but aesthetic outfits. Despite the weird looks, the fursuiter does not
+mind being in the spotlight. I respect that, as much as I respect people
+who wear their favorite band's merch from head to toe.
+
+The average person does not understand furries. Indeed, explaining the
+life choice of being a furry is less straightforward than explaining stamp
+collecting. Nevertheless, furries are unfettered. I respect that, as much
+as I respect fans who enjoy a starting band "just because".
+
+Furries are just people who happen to take an unconventional hobby that's
+higher-profile than usual, such as stamp collecting, and not remotely
+harmful, such as owning a fucking gun. I bet millions of non-furry people
+call them weirdos. But I think what they represent is the fraction of
+society who are just different, no more no less. I like things that are
+different. Ipso facto, I respect furries.
diff --git a/docs/random/img/2bugs1day/re.png b/docs/random/img/2bugs1day/re.png
new file mode 100644
index 0000000..209356b
--- /dev/null
+++ b/docs/random/img/2bugs1day/re.png
Binary files differ
diff --git a/docs/random/img/ergo_m575/bottom.jpg b/docs/random/img/ergo_m575/bottom.jpg
new file mode 100644
index 0000000..995c258
--- /dev/null
+++ b/docs/random/img/ergo_m575/bottom.jpg
Binary files differ
diff --git a/docs/random/img/ergo_m575/left.jpg b/docs/random/img/ergo_m575/left.jpg
new file mode 100644
index 0000000..e4dd519
--- /dev/null
+++ b/docs/random/img/ergo_m575/left.jpg
Binary files differ
diff --git a/docs/random/img/ergo_m575/rear.jpg b/docs/random/img/ergo_m575/rear.jpg
new file mode 100644
index 0000000..5016cc7
--- /dev/null
+++ b/docs/random/img/ergo_m575/rear.jpg
Binary files differ
diff --git a/docs/random/img/ergo_m575/right.jpg b/docs/random/img/ergo_m575/right.jpg
new file mode 100644
index 0000000..de46d42
--- /dev/null
+++ b/docs/random/img/ergo_m575/right.jpg
Binary files differ
diff --git a/docs/random/img/ergo_m575/socket.jpg b/docs/random/img/ergo_m575/socket.jpg
new file mode 100644
index 0000000..1658f68
--- /dev/null
+++ b/docs/random/img/ergo_m575/socket.jpg
Binary files differ
diff --git a/docs/random/img/ergo_m575/top.jpg b/docs/random/img/ergo_m575/top.jpg
new file mode 100644
index 0000000..d79990a
--- /dev/null
+++ b/docs/random/img/ergo_m575/top.jpg
Binary files differ
diff --git a/docs/random/img/ergo_m575/trackball.jpg b/docs/random/img/ergo_m575/trackball.jpg
new file mode 100644
index 0000000..c6870a4
--- /dev/null
+++ b/docs/random/img/ergo_m575/trackball.jpg
Binary files differ
diff --git a/docs/random/img/latex_handwriting/linalg_notes.png b/docs/random/img/latex_handwriting/linalg_notes.png
new file mode 100644
index 0000000..82e339a
--- /dev/null
+++ b/docs/random/img/latex_handwriting/linalg_notes.png
Binary files differ
diff --git a/docs/random/img/latex_handwriting/long_equation.png b/docs/random/img/latex_handwriting/long_equation.png
new file mode 100644
index 0000000..e6e419f
--- /dev/null
+++ b/docs/random/img/latex_handwriting/long_equation.png
Binary files differ
diff --git a/docs/random/img/shenzhen_io_review/canvas.png b/docs/random/img/shenzhen_io_review/canvas.png
new file mode 100644
index 0000000..6961448
--- /dev/null
+++ b/docs/random/img/shenzhen_io_review/canvas.png
Binary files differ
diff --git a/docs/random/img/shenzhen_io_review/sandwich_machine.png b/docs/random/img/shenzhen_io_review/sandwich_machine.png
new file mode 100644
index 0000000..3259e07
--- /dev/null
+++ b/docs/random/img/shenzhen_io_review/sandwich_machine.png
Binary files differ
diff --git a/docs/random/img/shenzhen_io_review/shoes.png b/docs/random/img/shenzhen_io_review/shoes.png
new file mode 100644
index 0000000..03ed4ce
--- /dev/null
+++ b/docs/random/img/shenzhen_io_review/shoes.png
Binary files differ
diff --git a/docs/random/img/shenzhen_io_review/shoes_notch.png b/docs/random/img/shenzhen_io_review/shoes_notch.png
new file mode 100644
index 0000000..55d8d2e
--- /dev/null
+++ b/docs/random/img/shenzhen_io_review/shoes_notch.png
Binary files differ
diff --git a/docs/random/img/shenzhen_io_review/shoes_wires.png b/docs/random/img/shenzhen_io_review/shoes_wires.png
new file mode 100644
index 0000000..4321716
--- /dev/null
+++ b/docs/random/img/shenzhen_io_review/shoes_wires.png
Binary files differ
diff --git a/docs/random/img/shenzhen_io_review/traffic_signal.png b/docs/random/img/shenzhen_io_review/traffic_signal.png
new file mode 100644
index 0000000..d7ead83
--- /dev/null
+++ b/docs/random/img/shenzhen_io_review/traffic_signal.png
Binary files differ
diff --git a/docs/random/img/shenzhen_io_review/traffic_signal_histograms.png b/docs/random/img/shenzhen_io_review/traffic_signal_histograms.png
new file mode 100644
index 0000000..1e2f45e
--- /dev/null
+++ b/docs/random/img/shenzhen_io_review/traffic_signal_histograms.png
Binary files differ
diff --git a/docs/random/img/miui_open_app_popup.png b/docs/random/img/smartphone_os/miui_open_app_popup.png
index 50a4589..50a4589 100644
--- a/docs/random/img/miui_open_app_popup.png
+++ b/docs/random/img/smartphone_os/miui_open_app_popup.png
Binary files differ
diff --git a/docs/random/img/o2os11_power_menu.png b/docs/random/img/smartphone_os/o2os11_power_menu.png
index 9dc352c..9dc352c 100644
--- a/docs/random/img/o2os11_power_menu.png
+++ b/docs/random/img/smartphone_os/o2os11_power_menu.png
Binary files differ
diff --git a/docs/random/img/o2os12_power_menu.png b/docs/random/img/smartphone_os/o2os12_power_menu.png
index 86891d4..86891d4 100644
--- a/docs/random/img/o2os12_power_menu.png
+++ b/docs/random/img/smartphone_os/o2os12_power_menu.png
Binary files differ
diff --git a/docs/random/img/tab_gang/braille_display.jpg b/docs/random/img/tab_gang/braille_display.jpg
new file mode 100644
index 0000000..ada96b4
--- /dev/null
+++ b/docs/random/img/tab_gang/braille_display.jpg
Binary files differ
diff --git a/docs/random/img/xkcdbot/post.png b/docs/random/img/xkcdbot/post.png
new file mode 100644
index 0000000..52d8e22
--- /dev/null
+++ b/docs/random/img/xkcdbot/post.png
Binary files differ
diff --git a/docs/random/img/xkcdbot/thumbnail.png b/docs/random/img/xkcdbot/thumbnail.png
new file mode 100644
index 0000000..270eb4f
--- /dev/null
+++ b/docs/random/img/xkcdbot/thumbnail.png
Binary files differ
diff --git a/docs/random/index.md b/docs/random/index.md
index 110cb76..34cf6b7 100644
--- a/docs/random/index.md
+++ b/docs/random/index.md
@@ -7,8 +7,17 @@ they just stay there, like malloc'd memory that will never be freed.
Nevertheless, occasionally I leave a permanent trace along the way.
-- [Gemini TL;DR](gemini_tldr)
-- [How an FKFD Comic is made](how_fkfd_is_made)
-- [The Real Git](realgit)
-- [The most important part of a smartphone is the OS.](smartphone_os)
-- [My Life Goals](life_goals)
+- [Gemini TL;DR](gemini_tldr.md)
+- [How an FKFD Comic is made](how_fkfd_is_made.md)
+- [The Real Git](realgit.md)
+- [The most important part of a smartphone is the OS.](smartphone_os.md)
+- [My Life Goals](life_goals.md)
+- [I Respect Furries](i_respect_furries.md)
+- [I'm Using a Trackball (Logitech ERGO M575)](ergo_m575.md)
+- [I Joined The Tab Gang](tab_gang.md)
+- [Better Handwritten Math with LaTeX](latex_handwriting.md)
+- [xkcdbot](xkcdbot.md)
+- [Of Potato Chips And Food Globalization](potato_chips.md)
+- [2 Bugs 1 Day](2bugs1day.md)
+- [Shenzhen I/O Game Review](shenzhen_io_review.md)
+- [2024-04-05](2024-04-05.md)
diff --git a/docs/random/latex_handwriting.md b/docs/random/latex_handwriting.md
new file mode 100644
index 0000000..f02bddf
--- /dev/null
+++ b/docs/random/latex_handwriting.md
@@ -0,0 +1,78 @@
+# Better Handwritten Math with LaTeX
+
+2022-09-04
+
+Argument: LaTeX improves your handwritten math.
+
+Put more precisely: If you are a first-year undergrad in science or
+engineering, practicing LaTeX is beneficial for your handwritten math.
+
+I began learning LaTeX in high school for mostly trivial tasks. In
+university however, my calculus professor awarded a 10% bonus to whichever
+group submitted their assignment in LaTeX. Unless circumstances forbid,
+I would be responsible for formatting my own and my teammates' handwriting
+into LaTeX, and secure the bonus even if I was ignorant math-wise.
+
+In the summer semester the same professor taught us linear algebra and
+multivariable calculus, and the bonus remained in effect. Meanwhile, as an
+experiment, I kept my linear algebra notes in LaTeX.
+
+![First two pages of notes](img/latex_handwriting/linalg_notes.png)
+
+Any LaTeX user knows there are two styles for math script: inline and
+display. To use these two options wisely is part of what makes a document
+"beautiful". For example, if I had an equation that looks like `x
+= expression = another expression`, I will consider:
+
+- How many equal signs are in there?
+- If I wrote it inline, how wide will it be?
+
+and, most importantly,
+
+- Will I need to label individual steps?
+
+This is a case where display is way better than inline:
+
+![A chained equation in display and inline style](img/latex_handwriting/long_equation.png)
+
+LaTeX is helpful here because you can always try again if you messed up.
+I learned to avoid overfull hboxes, and formed many other typesetting
+habits this way. I underwent the transition from quick & dirty scribbling
+to structured logical statements (compared to whatever shit math I was
+doing before).
+
+The magical thing is, __these habits aren't constrained to the keyboard;
+they will flow from your pentip also.__
+
+Later this semester we had a midterm exam, where we had to write on paper.
+I knew, with a little conscious thought, where or how large something
+should be even without LaTeX. It was as if I was compiling a LaTeX
+document from my head, except it was more like JIT interpreting. I would
+remember to:
+
+- Leave a margin
+- Align equations by equal sign
+- Label equations
+- Make parentheses large enough
+- Draw a tombstone after a finished proof
+- Write the real set in `mathbb` style
+
+Most of these are trivial, yes, but I believe they contributed to an
+answer sheet that at least went easier on the eye. There were
+significantly fewer scribbles and awkward linebreaks than I usually had in
+high school.
+
+Why is it so?
+
+Hypothesis: Donald Knuth, Leslie Lamport, and the folks who made amsmath
+were all virtually swimming in academia. They expected a math paper to
+look a certain way, and when they designed the (La)TeX ecosystem they made
+it the default. As such, when you are using LaTeX, you are basically
+reusing a template made by top scholars. And when you write LaTeX, you go
+their way by default. The error messages are super unintuitive, but if you
+don't get any, you can be pretty sure you're doing things right.
+
+Conclusion: LaTeX is not only a typesetting program for digital documents;
+it helps you preprocess your mathemetical thoughts before you commit it to
+any medium. If you practice LaTeX as a first-year undergrad, you will find
+handwriting math easier and prettier on the first try.
diff --git a/docs/random/life_goals.md b/docs/random/life_goals.md
index d85c413..712e6bd 100644
--- a/docs/random/life_goals.md
+++ b/docs/random/life_goals.md
@@ -1,6 +1,6 @@
# My Life Goals
-Last updated 2022-05-17
+Last updated 2023-11-05
My life sucks. That's why I constantly think about dying. Don't worry,
I won't do it *right* now, but it's a prudent act to list out all the
@@ -15,45 +15,83 @@ There's some cross-referencing among the items, so I've added labels in
__boldface__ and/or dependencies (dep) in parentheses where necessary.
## Art
-- Learn bass (__bass__)
+- <s>Learn bass</s> (__bass__) (learning it right now)
- Get enough vocal training to not sound terrible
- Learn basic music theory
+- <s>Record a song</s> I covered [Early Sunsets Over
+ Monroeville](../music/early-sunsets-over-monroeville.md) by My Chemical
+ Romance and wrote [This Song Will Uncure Your
+ Depression](../music/uncure-your-depression.md)
- Start playing drums again (__drum__)
- Join a rock band as bassist (dep: bass) or drummer (dep: drum)
-- Go to a twenty øne piløts concert in every album cycle (dep: us-uni)
-- Buy Trench (2018) on CD or cassette
-- Go to an IDKHOW concert (dep: us-uni)
+- Go to a twenty øne piløts concert (dep: umich)
+- <s>Buy Trench (2018) on CD or cassette</s> I bought a CD!
+- <s>Go to an IDKHOW concert (dep: umich)</s> Checked as of
+ [2024-04-05](2024-04-05.md)
+- Go to a MCR concert (dep: umich)
+- Go to a Green Day concert (dep: umich)
- Learn to properly rap the entirety of Car Radio
+- Learn to play Jesus Of Suburbia on bass
+- Learn to play Paranoid Android on bass
+- <s>Get more stickers</s>
+- Get a few figurines or little artifacts on my desktop
+- Get a hammer and a sickle to put on my wall
+- Commission an artist
+- Have something signed by, or take a photo with an artist I know
## University
- Get a bachelor's degree in ECE
-- Get a second BS and/or MS at a US university (__us-uni__)
+- <s>Get offer from UMich</s> (__umich__)
+- Get a bachelor's in CE at UMich
+- Get a master's
- Publish a paper of some sort somewhere and snail mail my friends signed
copies of it
-- Become head of tech dep't of student union
+- <s>Become head of tech dep't of student union</s> I made it through one
+ year of tenure
+- <s>Become TA of Intro to Comp Engr</s> I got the job and made it through
+ the semester!
## Engineering
-- Get access to a machine shop, learn to use the lathe, milling machine,
- and electric drill
+- <s>Get access to a machine shop</s> There is one in uni
+- <s>Use the lathe</s>
+- <s>Use milling machine</s>
+- <s>Use electric drill</s>
- Learn to make PCBs like a professional (__pro-pcb__, dep: reflow)
-- Get an oscilloscope and learn to use it
-- Get a reflow oven and learn to use it (__reflow__)
+- Get an oscilloscope
+- <s>Learn to use an oscilloscope</s>
+- <s>Learn reflow soldering (__reflow__)</s> Check out [Reflow
+ Workshop](../projects/reflow-workshop.md#2023-04-09-sunday)
+- <s>Host a reflow soldering workshop (dep: reflow)</s> (It's finally over)
- Try more embedded development
- Get a PinePhone Pro and hack it
-- This is gonna sound like an sponsorship but it's not. Get a Pinecil,
- PineTab, PinePower, PineNote and PineBuds
+- This is gonna sound like an sponsorship but it's not. Get
+ a <s>Pinecil</s>, PineTab, PinePower, PineNote and PineBuds
- Make a submarine. Around 2 liters in volume, uncrewed (obviously)
- Make my kid(s) a custom calculator to avoid those lame Casios, assuming
- they'd still use those for math 20 years later (dep: kid, pro-pcb)
+ they'd still use those for math 20 years later (__calc__, dep: kid,
+ pro-pcb)
- Go to my high school machine shop and help out my engineering teacher
and his FRC team
-- Host workshops about electronics (dep: pro-pcb)
- Learn to make better 3D models
- Learn to operate a 3D printer
- Design a PCB namecard that is functional in at least one way
- Document development of a project in a video
- Use the V-USB library somewhere
- Make a portable electronic musical instrument
+- Make a computer keyboard do something else while looking the same
+- Use an e-ink screen in a project, probably the custom calculator
+- Make an elliptical reflector dish
+- <s>Write some assembly</s> wrote some for EECS 370
+- <s>Try an ARM SBC</s> I just realized I have a Raspberry Pi
+- <s>Try writing STM32 C</s> did some of this for EECS 373. The code style
+ ST uses is __terrible__ (2 spaces, line break before curly brace,
+ `CamelAndSnake_Case`, uppercase function names)
+- Try a RISC-V SBC
+- <s>Make a weird-looking USB flash drive</s> I made one out of a covid
+ rapid test box
+- <s>Give someone something handmade as a gift</s> I gave a [blobcat
+ PCB](../projects/blobcat-pcb.md) to my friend
+- Use an Espressif SoC on a PCB
## Computer
- DIY a desktop computer, which conceivably will run Arch Linux and KDE
@@ -72,14 +110,16 @@ __boldface__ and/or dependencies (dep) in parentheses where necessary.
- Attend a convention of any kind
- Observe a furry convention (but not participate)
- Learn CW (a.k.a. morse code)
-- Get a ham radio license and buy a handheld station
+- <s>Get a ham radio license and buy a handheld station</s> DE BH4FXW 73
+ sent from my UV-5R
+- <s>Get an FCC ham radio license as well</s> KE8ZVB
- Eliminate as much meat as possible from diet
- Declare myself expert in at least one thing
- Own a cat (probably two)
- Read all books by Karl Marx
- Get a semi-professional filming rig with a greenscreen and stuff
- Have one or more kids (__kid__)
-- Get my own credit card
+- <s>Get my own credit card</s> (at least it's got my name on it)
- Install solar panels somewhere
- Seek revenge on that one roommate whose gaming noise keeps me up at
night
@@ -90,4 +130,6 @@ __boldface__ and/or dependencies (dep) in parentheses where necessary.
- Co-author an IETF RFC
- Get a tiny tattoo
- Restuff my blåhaj
-- Make something totally unrelated look like an electrolytic capacitor
+- Make something that is not, but looks like an electrolytic capacitor
+- Flip off a conservative
+- Do standup comedy at an open mic night
diff --git a/docs/random/potato_chips.md b/docs/random/potato_chips.md
new file mode 100644
index 0000000..2b7c80e
--- /dev/null
+++ b/docs/random/potato_chips.md
@@ -0,0 +1,243 @@
+# Of Potato Chips and Food Globalization
+
+Written 2021-12-04
+
+Published 2023-03-31
+
+## Disclaimer
+
+This is my argumentative essay for an academic writing course. It is
+_not_ a paper, and there are many debatable, uncited claims. My major has
+nothing to do with food or economics.
+
+I am now publishing this on my blog because I want to cite it in another
+assignment for a psychology course. If you are reading this, TA
+(thanks for your time btw), you may find a PDF
+[here](/static/potato_chips.pdf).
+
+If you are taking or about to take Prof. Joelle Tybon's VY100, I am
+obligated by Honor Code to warn you against plagiarism, as if my work
+would be any use to you.
+
+Otherwise, this essay is licensed under CC BY-NC 4.0.
+
+## Essay
+
+Potato chips are ubiquitous. These deep fried, crunchy and greasy slices, sealed
+in a bag or cylinder, are shelved in convenience stores of virtually every city.
+Despite bearing a reputation for being nutritionless, their value for research
+exceeds what one might expect from a cheap snack. Potato chips are a highly
+globalized processed food, thanks to international food corporations expanding
+their market. Yet on the other hand they are also localized, partly due to
+efforts by local branches of said corporations to cater to local demand,
+resulting in diverse flavors from market to market. In this paper I will use
+potato chips as a primary example to show that the popularity a flavor of
+processed food receives in a region depends on its culinary practices and the
+history of the food.
+
+A particularly interesting phenomenon is that many regions feature their own set
+of unique flavors that are hardly found anywhere else. One example is the duo
+Cucumber and Salt & Vinegar, respectively found in the Sinosphere and the
+Anglosphere, or, for the sake of simplicity, China and Britain. Cucumber
+flavored potato chips are not widely known or sold in Britain; salt & vinegar
+flavored potato chips are just the opposite, and the minority of Chinese eaters
+who do enjoy them, to this day, have to purchase them abroad or from specialized
+stores. Product reviews on _Amazon_ and _Taobao_ show that, of the two, each flavor
+available to one region receives generally negative feedback in the other.
+
+This observation shows that flavors are region-dependent. For a food
+manufacturer seeking the overseas market, simply copying all flavors of
+a processed food from its birthplace to another region would fail, especially if
+the two have little cultural overlap. There can be such unagreeable flavors
+that, no matter how heavily marketed, will not be appreciated overnight. It may
+take years, or even generations, for the latter region’s population to accept
+it. On the contrary, a brand new, never-before-imagined flavor could be invented
+there and turn out successful. What is it that determines the popularity of
+a flavor in a region, then? Evidently, taste is not the only reason, because
+there is no standard for it. This prompts us to search for social factors that
+consumers take into consideration when judging a flavor.
+
+Flavor preferences of processed food are closely connected to culinary
+practices, in other words, how people cook. Ostensibly this may sound bizzare,
+since processed food is commonly believed to be an escape from the kitchen, and
+a consumer of processed food does not necessaarily know how to cook. However,
+since food undergoes domestication to align better with local culture (Pilcher
+33), I will show via analysis that culinary practices affect the cultural
+perception of a certain ingredient, and thus of a flavor. The analysis focuses
+on four perspectives: familiarity, social status, variety, and combination.
+
+Familiarity is the most obvious of all. If a flavor is advertised to be made of
+an ingredient native to a region, it will face fewer hurdles on its way to
+popularity. China is the world’s biggest producer as well as consumer of
+cucumber, which means people are more familiar with this plant. Likewise,
+flavors involving cheese or onion are more popular in Western countries, and
+seeweed flavored chips originated in Japan. Substantial consumption is
+beneficial but optional, as Rath pointed out that a food can act as an agent of
+national identity whether it is a main staple or not (82). This could explain
+the rise of salted egg yolk, hot pot and red braised pork belly flavors in the
+Sinosphere — none of the three are staples, yet all are without a doubt
+representative of Chinese cuisine.
+
+Social status is the hierachy that a culture assigns to an ingredient. To
+paraphrase Warde, a prestigious taste situates a person in a higher social
+class, and vice versa (308). In the scope of highly commodified processed food,
+it is approximately proportional to its price or scarcity. Rationally, it makes
+more sense for a consumer to opt for an ingredient higher in status, given the
+fact that potato chips of common flavors cost roughly the same. Salt and vinegar
+as fundamental kitchen seasonings are both low in cost, and unlike fresh produce
+they are usually bought in bulk once every few months. Though poured into
+a large number of Chinese dishes, they are rarely considered vital. Except for
+a handful of special recipes, few would lay as much scrutiny on them as on the
+vegetables and meat. A flavor simply named “salt & vinegar”, as a consequence,
+is evaluated by Chinese consumers with a pinch of salt, and fall onto the
+unfavorable side of the spectrum for this reason.
+
+Another culinary practice consumers take into account is variety, that is, the
+gamut of flavors that an ingredient is expected to be presented in. The wider it
+is, the fewer expectations will be set when one is sampling a new kind of food
+consisting of the ingredient, and the fewer judgments will be made should it
+deviate from anticipation. The so-called cucumber flavoring in potato chips
+contains, as the ingredient list on the package clarifies, white sugar, salt,
+MSG, and a mixture of cucumber, cilantro, onion, vinegar, and soy sauce, giving
+the chips a slightly mint-like relish which is a major drawback according to
+those who abhor the flavor, most of whom living in a Western country. Throughout
+history Chinese people have incorporated cucumber into countless dishes, simple
+or fancy, cold or hot, insipid or savory, traditional or modern. This means the
+Chinese are more likely to accept the diversity of its taste, and thus more
+inclined to try cucumber flavored potato chips when given the opportunity. In
+Britain however, cucumber, unless pickled, is more often considered a flavorless
+fruit found in salads with little potential other than being eaten raw, and
+those who encounter the rebellious, mint-like version of cucumber flavor are
+caught off guard. As a result this culinary underdevelopment and
+underappreciation contributes to the fact that very few kinds of processed food
+in Britain are available in cucumber flavor.
+
+Last but not least, it is combination that amplifies judgment. In every cuisine
+there are unwritten rules which decree that certain ingredients may or may not
+coexist. Defying the rules is dangerous, as a wrong combination can form an
+unwelcome flavor that local people have never adapted to, and will not accept
+anytime soon. Salt and vinegar is one of such combinations; they rarely pair up
+on a Chinese recipe. Although some dishes do require the presence of both,
+additional seasoning is usually added. What people expect to accompany vinegar
+is usually not salt, but sugar, as evidenced by the classic Chinese dish _tangcu
+paigu_ (pork ribs in sugar and vinegar), most of whose salty taste comes from the
+brewed soy sauce (_shengchou_). The combination of salt and vinegar without
+a third companion would therefore disappoint Chinese eaters with blandness, in
+other words, the lack of “richness” most Chinese dishes strive to accumulate.
+
+Each of the four reasons listed above is capable of explaining certain phenomena
+pertaining to potato chips. But if we were to inspect one phenomenon
+specifically, some reasons would fail. For instance, our first reason, namely
+familiarity, does not play a dominant role in affecting the popularity of salt
+& vinegar flavor in China. Otherwise, the flavor would be extremely widespread
+in China due to their abundance in a Chinese kitchen and appearance in thousands
+of recipes. Therefore, their failure to gain market share in China suggests
+there must be something inherently unpleasant to the Chinese palate about this
+flavor, which is why we resort to the theory of combination.
+
+What is manifest here is that when a region makes its first encounter with an
+exotic flavor, culinary practices are the jury that examine whether or not the
+flavor belongs to local culture. Now the subsequent question is, if it does, for
+how long will it persist? The answer is surprisingly tautological in a way: The
+longer it has been, the longer it will be. To explain this we look at the
+history of the processed food, which matters because the assessment of a taste
+is formed upon historical ethnographies (Shapin 177). We can compare flavors of
+processed food to residents of a housing estate: Young dwellers come and go, but
+elder inhabitants rarely leave. In the same vein, the “elder” flavors assert
+their dominance in a market by being the first ones available, hence carrying
+historical or, sometimes, nostalgic value. The original-flavor potato chips from
+Lay’s Stax series are marketed as _zhongyu yuanwei_ (loyal to the original) in the
+Chinese Mainland market. The fact that loyalty to a flavor can be established
+implies its long history, which contributes to its unperishing popularity.
+Cucumber flavored potato chips are documented to have been sold in China since
+2004, which is 17 years prior to the time of writing, a considerable timespan in
+the history of China’s all-round modernization. It should come with no surprise
+that contemporary Chinese consumers by and large take it for granted that
+cucumber flavor is a legitimate and acceptable option for potato chips, and it
+is no longer possible to discontinue it without a backlash from enthusiasts.
+Though it is reasonable and often proper to attribute momentary blossom to
+a fad, few can dispute the competence of a flavor that has endured for decades.
+
+As to why a region has its own set of preferences, and there is no such flavor
+as a global favorite, we resort to the regionality of history. The countries
+involved in food globalization can be classified as “inventors” and “importers”,
+two terms I have contrived. The inventors define _what_ the food is, while the
+importers have a chance to add their touch to _how_ the food will taste like by
+rejecting some original flavors and creating new ones. In an inventor country,
+the very first “classic” flavors tend to prevail. Potato chips were invented in
+Britain in the 19th century; seasoning on potato chips emerged in the 1950’s in
+the same country (Joe “Spud” Murphy n.p.) Among the first few flavors that
+potato chips were produced in, there was salt & vinegar, which has remained
+popular in Britain ever since. They achieve as much popularity as longevity,
+eventually becoming an essential, unseverable part of the food itself.
+
+An importer country however, having missed the infancy of the processed food,
+does not necessarily respect its history whence it came. Instead, it keeps its
+own history relative to its time of arrival; it may also reject flavors that
+defy culinary practices it has passed on as a form of cultural heritage and
+demand a local variant to its own need (Pilcher 33). Venerable flavors in an
+inventor country thus lose their natural advantage once imported, and relatively
+recent flavors developed in the importer country find them less challenging to
+contend with and eventually overtake. To some extent, domestic flavors
+compensate for the exotic ones that failed the race, and the result is
+a distinct set of naturalized flavors that adhere to local culinary practices,
+formed gradually over the course of a shifting equilibrium.
+
+One could argue culture and history are not as important as they used to be in
+affecting the popularity of a flavor in an importer country. This is true, since
+the advent of big data and high internet coverage have made consumer preferences
+on social media over the past month crystal clear to merchants, whose only duty
+is to produce the trending flavors as quickly as possible (Cavish n.p.) What was
+once an inconceivable feat is now the norm. Being novelty-driven and built upon
+consumers’ curiosity, these extraordinarily creative flavors tend to be
+short-lived and reach out to an audience consisting of barely anyone else than
+urban young consumers. Hardly any of these flavors can achieve long-term
+survival in such a saturated market. The short attention span of modern
+consumers and social media is to blame, but the ultimate reason lies within the
+flavors themselves. They have never been domesticated, that is, adapted to the
+local palate, and given such a short lifespan, it is most likely the case that
+they will never be, before they are flushed away from the market. My best
+attempt at a metaphor here is a zoo: Visitors may admire the wild, “novel”
+animals on display, but few will keep one as a pet. In the long run, only
+flavors that truly respect local culture will receive the visa for a permanent
+stay, and then establish loyalty. Therefore, even in contemporary times, novelty
+and viral marketing cannot override culinary practices and history.
+
+Junk food as potato chips are, they are an epitome of how a processed food is
+globalized and, as I have analyzed in this paper, how its flavors are assessed
+by consumers inside and outside of its birthplace. The reasons listed are not
+exhaustive, nor can I claim my theories to be comprehensive. I have certainly
+overlooked many intrinsic complexities of food globalization, and due to my
+limited knowledge and a Chinese background with chiefly Euro-American influence,
+my arguments may be overturned once a third culture is taken into account.
+Nevertheless, I hope this paper serves as food for thought for whoever wishes to
+investigate this subject in greater detail.
+
+## Works Cited
+
+- Cavish, Christopher St. “Durian Pizza: When Big Data Writes the Menu.”
+ _Radii_, 22 February 2018,
+ [radiichina.com/durian-pizza-when-big-data-writes-the-menu](https://radiichina.com/durian-pizza-when-big-data-writes-the-menu).
+ Accessed 4 December 2021.
+
+- “Joe ‘Spud’ Murphy: The Man Who Gave Potato Chips Flavor.” _HuffPost_,
+ [www.huffpost.com/entry/joe-spud-murphy-the-man-w_n_1437270](https://www.huffpost.com/entry/joe-spud-murphy-the-man-w_n_1437270).
+ Accessed 20 November 2021.
+
+- Pilcher, Jeffrey M. “‘Tastes Like Horse Piss’: Asian Encounters with
+ European Beer.” _Gastronomica_, vol. 16, no. 1, 2016, pp. 28–40.
+ _JSTOR_,
+ [www.jstor.org/stable/26362318](https://www.jstor.org/stable/26362318).
+ Accessed 29 November 2021.
+
+- Rath, Eric C. “Historical Reflections on Culinary Globalization in East
+ Asia.” _Gastronomica_, vol. 17, no. 3, 2017, pp. 82–84. _JSTOR_,
+ [www.jstor.org/stable/26362463](https://www.jstor.org/stable/26362463).
+ Accessed 28 November 2021.
+
+- Shapin, Steven. “The Sciences of Subjectivity.” _Social Studies of
+ Science_, vol. 42, no. 2, 2012, pp. 170–184.
+
+- Warde, Alan. “Eating Globally: Cultural Flows and the Spread of Ethnic
+ Restaurants.” _The Ends of Globalization: Bringing Society Back In_, ed.
+ Don Kalb et al., Rowman & Littlefield, 2000, pp. 299–316.
diff --git a/docs/random/shenzhen_io_review.md b/docs/random/shenzhen_io_review.md
new file mode 100644
index 0000000..e4287cb
--- /dev/null
+++ b/docs/random/shenzhen_io_review.md
@@ -0,0 +1,268 @@
+# Shenzhen I/O Game Review
+
+I don't usually game.<sup>[citation needed]</sup>
+
+Sometime in 2021, in my first days of university, I felt bored and bought
+this game. Also out of vanity to show how different I am from my league of
+legends roommates. (it didn't work because i got stuck at assignment eight
+and gave up, until december 2023 when i decided to give it another spin
+now that i've made a few PCBs and written some assembly in real life)
+
+## What is this game
+
+[Shenzhen I/O](https://www.zachtronics.com/shenzhen-io/) is a "nerd puzzle
+game" (my words) by Zachtronics.
+
+You move to Shenzhen and work as an employee at 龙腾 (Longteng), where you
+combine chips into circuits that meet client specifications. In each level
+(assignment), you are given a canvas like this one:
+
+![Blank circuit board next to a waveform viewer and part
+selector](img/shenzhen_io_review/canvas.png)
+
+Then you complete these tasks, in the general order:
+
+- Drag parts from the sidebar
+- Connect parts with each other and I/O pins on the sides
+- Write assembly for the programmable chips
+- Simulate and debug until output matches expected waveform
+
+The assembly language in question is invented specifically for the game.
+The chips are fictional, too. It doesn't actually require prior embedded
+systems knowledge. A few differences from real-life embedded systems:
+
+- No need to explicitly power the chips
+- No need to worry about clock cycles
+- Everything seems to use decimal, not binary
+- Hardly any boolean operations
+- Voltage levels range from 0 to 100
+- ADC/DAC are perfectly accurate
+- Integers range from -999 to 999
+- MCUs have two registers max (`acc` and `dat`), and no SRAM
+- XBus, the asynchronous protocol, seems too complicated for one clock
+ cycle
+- Multidriver problems are handled simply by `input = max({outputs})`, and
+ can be abused
+
+And mostly importantly, the debugger is something I wish I had in real
+life. You can literally step through the instructions! Imagine if you were
+able to freeze time, poke an ATmega328P and know which loop it's stuck in.
+
+That said, I'm not the best at this game. Hell, I'm not even _good_, as
+I have created abominations like:
+
+<details markdown="1">
+<summary>Spoiler</summary>
+![Circuit with five MCUs and wires that almost fill the entire canvas](img/shenzhen_io_review/traffic_signal.png)
+</details>
+
+After you complete each assignment, the game shows you where you are among
+all the players. The data I got was:
+
+![Three histograms, showing my data float much worse than
+average](img/shenzhen_io_review/traffic_signal_histograms.png)
+
+The current best for three metrics on the leaderboard are:
+
+- Lowest cost: 6 yuan (mine costs 20)
+- Power usage: 123 (mine uses 1.7K)
+- Lines of code: 9 (mine has 45)
+
+As you can see, my solution is far from good in whichever way, but
+nevertheless it works. This is because there are so many ways to solve
+a problem, and to optimize a solution.
+
+That said, am I going to optimize my solution? No.
+
+## Why is it fun
+
+Like all puzzle games, it is fun because it's intellectually challenging.
+It's not the kind of Genshin Impact minigame.
+
+Another reason is the plot. For most of the game's target audience, living
+in Shenzhen would be a foreign experience, and it's a lot of fun reading
+coworkers (both Chinese and from abroad) react to projects and life. The
+game's UI involves a mail client, where you receive emails from your
+supervisors and coworkers, and honestly that's half the fun.
+
+(lets ignore the fact that most chinese companies dont discuss everything
+over email)
+
+<details markdown="1">
+<summary>Spoiler alert!</summary>
+
+My favorite emails are:
+
+- When you designed a vape as merchandise for musician Cool Dad, who, once
+ the vapes were manufactured, got arrested for possession of
+ amphetamines. Also, Carl absolutely _roasted_ Cool Dad's music.
+- When you designed a laser/illuminator apparatus for law enforcement
+ weapons and Carl responded with brutal sarcasm out of moral concerns.
+ I kinda wonder if the correct move is _not_ to design it, but it's too
+ late.
+- When Joe proposed a haunted doll implemented with unmarked chips of
+ unknown origin, and was then forbidden from venturing further in the
+ field of "ghost electronics"
+- When a Japanese company claims to have developed a "traditional yarrow
+ stalk _I Ching_ divination" with "MEMS technology"
+</details>
+
+## Why is it hard
+
+### You have to read the datasheet
+
+The game comes with no manual; instead it ships a PDF containing all the
+datasheets and supplementary materials. Fortunately they are much easier
+to understand than those in real life. And they are (mostly) typeset very
+well. Much more pleasant to the eye than many actual industrial
+datasheets. In the one case it isn't, it's definitely intentional for
+a "sketchy" vibe.
+
+### Chips are intentionally underpowered
+
+We know modern chips are extremely powerful. So powerful it would render
+the game pointless. So the chips and assembly language in this game are
+intentionally limited. The smaller chip only holds 9 instructions, the
+larger one 14.
+
+I sometimes find it hard to implement any meaningful logic with such an
+underpowered chip. Once I found a solution that was 15 lines long, but
+I had to split them in two chips, and then I had to write extra code so
+they could communicate, which is another 10+ lines of overhead.
+
+Hardware-wise, the chip has 6 pins max, and you can't just use _any_ pin,
+because some pins are "simple", and some are "XBus".
+
+### Conditional instructions are cursed
+
+There's no actual "conditional branch". Give the chip a condition (e.g.
+`teq acc 1` tests if `acc == 1`) and it sets a global state (+/-). Then
+you can prepend a +/- to an instruction so that it only runs in the state.
+The global state makes it really hard to emulate nested `if` statements.
+Pretty sure there were actual ISAs that used this mechanism, but I always
+get confused on this logic.
+
+### Labels count as lines?
+
+If you wanted to jump to an instruction, you have to label the line, like
+
+```
+loop:
+ add 1
+ jmp loop
+```
+
+It should make sense to any programmer that labels are not real. They are
+just a fancy way to refer to the line number it precedes, and thus should
+not count as a line. But in the above code it does, because the text area
+only has so many rows. This leaves me with one less line for actual
+instructions. It took me way too long to realize that this is legal:
+
+```
+loop: add 1
+ jmp loop
+```
+
+The datasheet never mentioned this! I was looking at the
+[leaderboard](https://www.reddit.com/r/shenzhenIO/wiki/index/) when
+I found out.
+
+### Canvas size is limited
+
+The wire traces take a significant amount of space and there is only one
+side, so no vias. They will sometimes block pins. The only way to cross
+two wires is to use a bridge, which only comes in one size and direction.
+I have more than once made a circuit that looks like an absolute jumbled
+mess. Like what even is this
+
+<details markdown="1">
+<summary>Spoiler</summary>
+![Circuit with four bridges](img/shenzhen_io_review/sandwich_machine.png)
+</details>
+
+There was one level where I just _can't_ get the wires to go where they
+should. I tried every direction possible to route the wires, but there's
+either not enough horizontal space or enough vertical space. The most
+disgusting thing is this kind of notch that you can't route wires through.
+These notches made two hours of my life miserable. I almost gave up.
+
+![Tiny notch in circuit board with wires around it, but not through it](img/shenzhen_io_review/shoes_notch.png)
+
+Then I downloaded a solution from the leaderboard, only to find that, like
+in real life, you can route traces _below_ the chips. _Nobody mentioned
+that!_ There was nothing in the manual that explicitly said this was ok,
+nor was there any hint in the game. The hack is only accessible if you
+hold Tab, which enters "show wires" mode, and the chips become translucent
+so you can route traces underneath.
+
+<details markdown="1">
+<summary>Spoiler</summary>
+Normal view:
+
+![Circuit board with chips and wires](img/shenzhen_io_review/shoes.png)
+
+When holding Tab:
+
+![Same circuit but wires under chips are visible](img/shenzhen_io_review/shoes_wires.png)
+</details>
+
+I doubt this assignment is possible without this kind of hack. Anyway,
+I deleted the leaderboard solution and made my own version, and I'm never
+looking back.
+
+## Hardest assignments
+
+These are a list of assignments that I felt stuck on for more than an
+hour.
+
+<details markdown="1">
+<summary>Spoiler</summary>
+
+- Virtual reality buzzer (the level that led me to quitting in 2021)
+- Precision food scale
+- Traffic signal
+- Color changing shoes (titled "Would you believe it?" in game)
+</details>
+
+Again, I'm not good at this. If I had to find an excuse, it would be that
+I try my best to come up with the most elegant solution possible, however
+many lines it takes. I'd use a divide & conquer approach that's as
+symmetric as possible, which is not always optimal, and sometimes uses up
+too much of space and makes a mess of wires.
+
+## A nitpick on the awkwardness of languages
+
+The game is created by a bunch of Americans, for, I suppose, a primarily
+English-speaking audience. Despite this, the game and the datasheets are
+available in both English and simplified Chinese. The problems are:
+
+- The Chinese does not sound Chinese
+- The English does not sound Chinese
+
+If you tried to play the Chinese version as a Chinese person, as I have,
+you will find the Chinese dialog awkward. The translator did their best,
+but it sounds un-Chinese.
+
+They are grammatically correct, but I find this sort of conversation out
+of place in a Chinese workplace. However, I would prefer if real life
+workplace conversations sounded like this. Light-hearted without much
+客套话 (formalities). Let's hope society evolves that way.
+
+On the other hand, if you play in English, you'll find that your Chinese
+coworkers have extraordinarily high English competency, atypical of
+current day Shenzhen (but the game is set in the future, so one can hope).
+
+## Minigame: Shenzhen Solitaire
+
+Early in the game a coworker introduces a solitaire-style game with
+Mahjong-themed cards. It's a way to relax when I'm stressed out solving
+a puzzle. I like it so much that I [rewrote it in
+Rust](https://git.sr.ht/~fkfd/shenzhen_solitaire).
+
+## Conclusion
+
+At the time of writing, I have not finished the game (yet). So far it's
+really enjoyable. I did stress myself out a handful times halfway; that
+was because I was not treating it as a game. Once I accepted the fact that
+I'm just a casual player and being unable to solve the puzzles does not
+result in any loss of self worth, I was again ready to have fun.
diff --git a/docs/random/smartphone_os.md b/docs/random/smartphone_os.md
index b293ec4..e7342e9 100644
--- a/docs/random/smartphone_os.md
+++ b/docs/random/smartphone_os.md
@@ -102,7 +102,7 @@ consent. A side effect is that custom launchers, whose job is basically
opening third-party apps, are extremely painful to use.
![Allow "Nova Launcher" to open "VLC Media Player"?
-Allow/Deny](img/miui_open_app_popup.png)
+Allow/Deny](img/smartphone_os/miui_open_app_popup.png)
I believe this was out of good intention, but laying too much restriction
on third-party apps is impeding customization.
@@ -148,12 +148,12 @@ __Don't change muscle memory-driven interfaces.__
Here's an example. This is a mockup of the power menu on OxygenOS 11:
![Three buttons: Emergency (in red), Lock down, and Power Off. A menu is
-expanded, revealing "Restart"](img/o2os11_power_menu.png)
+expanded, revealing "Restart"](img/smartphone_os/o2os11_power_menu.png)
And this is what it looks like on OxygenOS 12:
![Three buttons: Emergency (in red), Power Off, and
-Restart](img/o2os12_power_menu.png)
+Restart](img/smartphone_os/o2os12_power_menu.png)
For an estimated 160 nights, I went to bed, held down the power button,
pressed the righthand rectangle, and listened to the vibration my phone
diff --git a/docs/random/tab_gang.md b/docs/random/tab_gang.md
new file mode 100644
index 0000000..8c40cd2
--- /dev/null
+++ b/docs/random/tab_gang.md
@@ -0,0 +1,44 @@
+# I Joined The Tab Gang
+
+2022-08-20 | CC BY-SA 4.0
+
+If you've done a considerable amount of programming, you must have heard
+of the Space vs. Tab battle, and perhaps picked a side. In personal hobby
+projects, this is mostly a choice of aesthetics. Ever since 13 or
+something I had been using 4 spaces, but on 2022-08-12, I am announcing my
+departure from this style for the tab character. (With the notable
+exception of Python)
+
+For too long have I been fooled by the Space Gang. Portable? Yes, but so
+are tabs. Configurable? Yes, but tabs more so. Compared to 4 spaces, tab
+has three advantages that won me over:
+
+1. 3 fewer bytes each indentation level;
+2. No need to worry about accidental 3- and 5-space indents;
+3. 3 fewer characters on a Braille display.
+
+1 and 2 are good reasons, yes, but the third one really moved me the way
+a truck tows a car in the disabled spot. Per Wikipedia, a Braille display
+looks like this:
+
+![A refreshable Braille display](img/tab_gang/braille_display.jpg)
+
+This one is 40 characters wide but 80-character models are sold. Screen
+space is precious. Imagine dragging your finger across the line of code,
+only to find the first 16 characters wasted on indentation using 4 levels
+of 4 spaces. It would be different if you used tabs. A tab is a single
+character, so 4 tabs would only use 4 braille cells, leaving the remaining
+36 cells for code. (Note that, however, I do not know any visually
+disabled person to testify this claim.)
+
+The lesson? When you find yourself weighing two options over an issue you
+think makes little difference to you, try considering how it affects
+people who live a different life from yours. Like how you design your
+"Accept/Decline call" UI on a mobile phone so that a senior person knows
+instantly what to do, instead of wondering if they should click, drag, or
+swipe. This kind of decision should not be made in a condescending manner.
+Instead, you and I should pay attention to the lives of different people:
+the technologically illiterate, the homeless, the disabled, and everyone
+you think deserves a better life than they have, and make informed
+decisions when you have the chance. Sometimes it's as simple as `set
+noexpandtab`.
diff --git a/docs/random/xkcdbot.md b/docs/random/xkcdbot.md
new file mode 100644
index 0000000..48d75ff
--- /dev/null
+++ b/docs/random/xkcdbot.md
@@ -0,0 +1,152 @@
+# xkcdbot
+
+First version 2022-11-29
+
+Updated 2023-12-15
+
+![A Mastodon post with comic number, title, title text, xkcd.com URL and
+explainxkcd URL; a reply below reads "Heads-up for my subscribers:" then
+a bunch of users](img/xkcdbot/post.png)
+
+## Birth
+
+Back in 2019, I signed up for a Mastodon account on mastodon.technology.
+Soon I began looking around for xkcd bots. There were several, but most of
+them were defunct. I would soon make up my mind to create my own.
+
+I have little memory, but per the
+[git log](https://github.com/fakefred/xkcdbot/commits/master) I finished
+it in 9 days. It ran on Node.js, which unfortunately was my favorite tech
+stack back then.
+
+I registered [@xkcdbot@botsin.space](https://botsin.space/@xkcdbot), then
+daemonized the bot on the same server I hosted my comics (which, for which
+I apologize, was also on a Node.js server). I wrote this bio:
+
+>I'm an xkcd bot who shows up at Randall's house at 3.3 millihertz and
+>asks "Hello, did you publish a new comic?" I occasionally dress like
+>a velociraptor just to freak him out.
+
+>If he admits he has drawn a new comic, I seize it and post it right here
+>before Randall shoots me down with a drone.
+
+## Rise
+
+The bot account was created on 2019-08-19.
+
+On 2022-10-28, xkcdbot gained 1k followers. It took 1166 days.
+
+On 2022-11-23, the number grew to 2k. It only took 26 more days.
+
+Of course, the surge owes to Elon Musk for pissing off so many people off
+Twitter, but I'm sure there are two more reasons as well:
+
+### Killer features
+
+- 5 minute polling interval
+- explainxkcd link
+- DM for "subscribers"
+- Comic thumbnail is focused on top left, so no spoilers
+
+![Only the left-hand side of the comic is shown in the preview](img/xkcdbot/thumbnail.png)
+
+### Administration
+
+The bot lives by a few simple rules. It:
+
+- does its damn job
+- has near-perfect uptime
+- never solicits followers
+- never interacts with people unless asked to
+- has no new features for three years
+
+## Accidents
+
+My code broke in production exactly once. There were other accidents, but
+they were all due to botsin.space downtime or Randall uploading the wrong
+comic.
+
+## Conclusion
+
+I believe this resolves all remaining questions on this topic. No further
+research is needed.
+
+## Update (2023-10-22)
+
+It turns out this does not resolve all remaining questions on this topic,
+and further research is needed.
+
+The problem is accessibility. On xkcd.com, if you hover your mouse on the
+comic, a tooltip shows up. This is known as "hover text" or "title text",
+which uses the "title" HTML attribute. In 2019 I wanted to create
+a similar experience, so I put it in the alt text.
+
+However it wasn't the intended purpose. The alt text is meant for:
+
+- A replacement when the image cannot be displayed
+- Assistive technology such as screen readers
+
+On the fediverse it's mostly the latter. Right now, the awesome [Sam
+Levine](https://botsin.space/@SRLevine@urbanists.social) is doing the
+noble work of replying to every post with a captioned version of the same
+comic.
+
+But it is a workaround. Around my reluctance to touch my rotten codebase,
+my incompetence as a developer, and my lack of interest to caption the
+comics myself. Whenever I post on my main handle, I make sure to caption
+my images and audio. But sadly xkcdbot cannot do that right now.
+
+> Why not?
+
+Because Randall doesn't caption comics when they are posted.
+
+> Can't you fetch the caption from explainxkcd?
+
+That might take hours before it's ready. Plus, caption might be incomplete
+and susceptible to vandals.
+
+> Just delay the post until the caption is ready!
+
+We don't know how long it will take. Also, I created the bot in order to
+read xkcd as soon as possible. I cannot accept the delay.
+
+> Just edit the post!
+
+I made the bot in 2019, when "edit" wasn't a thing. I _could_ make this
+feature right now, but I haven't touched my codebase for 4 years, and
+I don't use Node.js anymore.
+
+## Conclusion
+
+Accessibility for a bot is not an easy task. It's not a tech problem; it's
+a matter of community. I admit I cannot solve it by myself. Therefore,
+I am going to ask for the community for help.
+
+To be continued.
+
+## Update (2023-10-25)
+
+This is fire! I reached out to Sam Levine, who agreed to co-maintain
+xkcdbot and provide alt text with Mastodon's edit feature.
+
+I had intended to send a PGP-encrypted email, but Sam does not have that
+or any E2EE. So after consideration, I committed the cardinal sin of
+cybersecurity: I sent him* the password in plaintext via email.
+
+_i realized later that he/him might not be the best pronoun but they said
+they're okay with any. i really oughtta be careful next time_
+
+Say what you will but a bot that posts comics is just too trivial for
+a lecture on asymmetric key cryptography. Like sure a hacker could get
+access to the bot. Now what? They lock me and Sam out of it?
+
+- If they violate the TOS, we can certainly report it
+- If they make it obvious that the account's been hacked, people would
+ know
+- If they don't, they are impersonating us, which is pretty impressive
+ given they are doing two people's jobs in one
+
+I will now contact botsin.space's admin to know if we can recover the bot
+in case that happens, but I doubt it ever will.
+
+Update same day: Colin (botsin.space admin) said yes 🔥
diff --git a/docs/shitpost/cringiest_song.md b/docs/shitpost/cringiest_song.md
new file mode 100644
index 0000000..c02f822
--- /dev/null
+++ b/docs/shitpost/cringiest_song.md
@@ -0,0 +1,43 @@
+# Cringiest song I've ever heard
+
+2023-07-30
+
+Many songs are cringe, so much as to become a meme. This blogpost is about
+none of that. It is about a song that is cringe to _me_, even though
+I only ever heard it once.
+
+Cue ten-year-old me. My elementary school took all the students to
+a theater. It was probably Teacher's Day, hence the afternoon off. There
+was neither movie or play, just an assortment of performances to varying
+degrees of entertainment. If I remember correctly, there was a magician
+who claims to be related to Liu Qian. I had no idea what she was doing.
+
+At some point the host (That Guy) decided to sing an ode to our teachers.
+It was a rendition of a very mainstream pop song. I did not know, but
+I was about to hear _the_ cringiest song. The verse is some generic pickup
+lines and the chorus goes (translated):
+
+> My beloved teacher, I love you
+> Let me walk into your world, and be together with you
+> My beloved teacher, I love you
+> I will do anything for you forever, in this life and the next
+
+At this point we discovered that this was exactly the original song, but
+with the word "teacher" instead of "girl". Not only that, we were all
+asked to sing along to the "I love you". I might have cooperated (oops).
+What's worse, the teachers were present, which adds to the cringe factor.
+
+By changing one word, That Guy managed to transform an otherwise innocent
+romantic song into pure horror. Let me clarify. I do _not_ want to walk
+into my teacher's world. I do _not_ want to be "together" with my
+teachers. They're real nice, yeah, but that doesn't mean I will "do
+anything" or reincarnate myself for them, no thanks.
+
+The whole theater was filled to the brim with cringe. I wonder how they
+approved that song. They had one program to celebrate Teacher's Day, and
+they celebrated in the worst way possible.
+
+The difference between this song and memes is that memes are funny. It's
+just cringe. And it is no longer possible to recreate it. I can tell this
+story to you as a joke, and you'd be like, "haha, the song sure is
+terrible", but you can't experience the damage it did to a pool of kids.
diff --git a/docs/shitpost/haiku_2.md b/docs/shitpost/haiku_2.md
new file mode 100644
index 0000000..1c6d134
--- /dev/null
+++ b/docs/shitpost/haiku_2.md
@@ -0,0 +1,34 @@
+# Haiku collection 2
+
+2022-07-16
+
+> Another week's passed
+What have I achieved thus far?
+I aged one more week
+
+2023-06-06
+
+> To be useful is
+To be used by someone else
+Better be useless
+
+> In 2040,
+You will charge your EV with
+USB-PD
+
+After letting them sit in my notes for months, I finally sublimated the
+following obscene ideas into pure forms of art.
+
+> Cows in ISS
+Drink recycled piss to make
+Milk for astronauts
+
+> Fisher and Pearson
+Are fathers of statistics?
+Did they have gay sex?
+
+2023-07-04
+
+> "Violence is never…"
+Shut the fuck up, centralist
+Let me punch Nazis
diff --git a/docs/shitpost/index.md b/docs/shitpost/index.md
index 80401c4..acc4825 100644
--- a/docs/shitpost/index.md
+++ b/docs/shitpost/index.md
@@ -5,8 +5,11 @@ value, I know you are very bored. You may attempt but miserably fail to
entertain yourself with one or more of the following shitposts in the
precious time you could've spent more wisely elsewhere:
-- [Debunking the "fake moon landing" conspiracy theory](fake_moon_landing)
-- [Haiku collection](haiku)
-- [Rousseau and Schopenhauer walk into a bar](rousseau_and_schopenhauer)
-- [Extreme Boredom Has Driven Me Into Playing Pokemon Sword](pokemon_sword)
-- [The Egg is Flat](flat_egg)
+- [Debunking the "fake moon landing" conspiracy theory](fake_moon_landing.md)
+- [Haiku collection](haiku.md)
+- [Rousseau and Schopenhauer walk into a bar](rousseau_and_schopenhauer.md)
+- [Extreme Boredom Has Driven Me Into Playing Pokemon Sword](pokemon_sword.md)
+- [The Egg is Flat](flat_egg.md)
+- [Haiku collection 2](haiku_2.md)
+- [Writing A Blogpost Without Using Backspace](no_backspace.md)
+- [Cringiest song I've ever heard](cringiest_song.md)
diff --git a/docs/shitpost/no_backspace.md b/docs/shitpost/no_backspace.md
new file mode 100644
index 0000000..4be83fc
--- /dev/null
+++ b/docs/shitpost/no_backspace.md
@@ -0,0 +1,36 @@
+# Writing A Blogpost Without Using Backspace
+
+2022-07-16
+
+I'm a terrible typist. Even if I;m looking right at the keyboard, I can;t
+guarantee 100% accuracy. See? I got both apostrophes wrong. Truth be told,
+I never learned proper fingerring. That "rr" was a mistake,. That ",." was
+a mistake. OK, back to where we're at. Sometimes I plavce my finfers on
+the home row, and ten seconds later they just disperse onto random keys.
+Sometimes I press two keyts when I meant to press one. This is very common
+when I save a file in vim. The correct command is `:w`; I often do `:we`
+without knowing and vim telsl me there's no sucj command. And I ofeen get
+the keystroke ordering wrong. It's as if one hand has 50 more milliseconds
+of latency than the other.
+
+I never learned to blinf type either. Every ten seconds or so, I *have* to
+check out where my hands are ion the keyboard ior they starty doing weuird
+trhings and mess tup rtberu7thign I ;k doing. I have, at least one time,
+churned out a completye senmtence uninterrupted without looking at the
+keyboard, as if directly from brain toi keytboard. this rarely happens.
+
+What's thwe most painful is writing LaTeX. In math mode there's all these
+backslash-initiated commsnds and delimiters, sucha s `\left( \right)` and
+`\mathrm`. Typing all these while thinking about math is like soldering
+0603 resistors in a moving car. The good nerws is I have a plugin
+installed named VimTex, which helps me with some keystrokes, sucg as `]]`
+to match `\begin{}` with `\end{}`. But believe me, even with this one
+I cannot finish one equation woithjout constantly hittig backspace.
+
+There, I typoed a blogpost wuithout using backaspace. I have definitely
+proven what a terrible typ8sty I am. I don'ty deserve keyboards, and at
+this point I wonder why no one has taken them away. Dvorak won't save me,
+nor will it save anyone who is not equipped with enough dexterity to
+distinguish themselves from an ape. This would serve as a cautionary tale
+for your children to start typing early, or forerver be bound to a fate of
+sloppy typistry.
diff --git a/docs/ta/img/vg151_e1/graph_color.png b/docs/ta/img/vg151_e1/graph_color.png
new file mode 100644
index 0000000..6d7541e
--- /dev/null
+++ b/docs/ta/img/vg151_e1/graph_color.png
Binary files differ
diff --git a/docs/ta/img/vg151_e1/graph_hsl.png b/docs/ta/img/vg151_e1/graph_hsl.png
new file mode 100644
index 0000000..97f351e
--- /dev/null
+++ b/docs/ta/img/vg151_e1/graph_hsl.png
Binary files differ
diff --git a/docs/ta/img/vg151_e1/graph_hsv.png b/docs/ta/img/vg151_e1/graph_hsv.png
new file mode 100644
index 0000000..93d5c69
--- /dev/null
+++ b/docs/ta/img/vg151_e1/graph_hsv.png
Binary files differ
diff --git a/docs/ta/img/vg151_e1/graph_printed.png b/docs/ta/img/vg151_e1/graph_printed.png
new file mode 100644
index 0000000..773d2b7
--- /dev/null
+++ b/docs/ta/img/vg151_e1/graph_printed.png
Binary files differ
diff --git a/docs/ta/img/vg151_e1/sierpinski.png b/docs/ta/img/vg151_e1/sierpinski.png
new file mode 100644
index 0000000..fba2f5d
--- /dev/null
+++ b/docs/ta/img/vg151_e1/sierpinski.png
Binary files differ
diff --git a/docs/ta/img/vg151_funny_shit/deleted_quiz.png b/docs/ta/img/vg151_funny_shit/deleted_quiz.png
new file mode 100644
index 0000000..6a9e475
--- /dev/null
+++ b/docs/ta/img/vg151_funny_shit/deleted_quiz.png
Binary files differ
diff --git a/docs/ta/img/vg151_funny_shit/jiang_avi.png b/docs/ta/img/vg151_funny_shit/jiang_avi.png
new file mode 100644
index 0000000..599225c
--- /dev/null
+++ b/docs/ta/img/vg151_funny_shit/jiang_avi.png
Binary files differ
diff --git a/docs/ta/img/vg151_funny_shit/minamike.png b/docs/ta/img/vg151_funny_shit/minamike.png
new file mode 100644
index 0000000..60e7049
--- /dev/null
+++ b/docs/ta/img/vg151_funny_shit/minamike.png
Binary files differ
diff --git a/docs/ta/img/vg151_funny_shit/mm_emoticon.png b/docs/ta/img/vg151_funny_shit/mm_emoticon.png
new file mode 100644
index 0000000..f1b759b
--- /dev/null
+++ b/docs/ta/img/vg151_funny_shit/mm_emoticon.png
Binary files differ
diff --git a/docs/ta/img/vg151_funny_shit/moss_example.png b/docs/ta/img/vg151_funny_shit/moss_example.png
new file mode 100644
index 0000000..6494e08
--- /dev/null
+++ b/docs/ta/img/vg151_funny_shit/moss_example.png
Binary files differ
diff --git a/docs/ta/img/vg151_funny_shit/moss_reenactment.png b/docs/ta/img/vg151_funny_shit/moss_reenactment.png
new file mode 100644
index 0000000..d05fb1b
--- /dev/null
+++ b/docs/ta/img/vg151_funny_shit/moss_reenactment.png
Binary files differ
diff --git a/docs/ta/img/vg151_funny_shit/nuclear_acid.png b/docs/ta/img/vg151_funny_shit/nuclear_acid.png
new file mode 100644
index 0000000..6f8a0a8
--- /dev/null
+++ b/docs/ta/img/vg151_funny_shit/nuclear_acid.png
Binary files differ
diff --git a/docs/ta/img/vg151_funny_shit/p2_code.png b/docs/ta/img/vg151_funny_shit/p2_code.png
new file mode 100644
index 0000000..505ef52
--- /dev/null
+++ b/docs/ta/img/vg151_funny_shit/p2_code.png
Binary files differ
diff --git a/docs/ta/img/vg151_funny_shit/ssh-keygen.png b/docs/ta/img/vg151_funny_shit/ssh-keygen.png
new file mode 100644
index 0000000..ddbd425
--- /dev/null
+++ b/docs/ta/img/vg151_funny_shit/ssh-keygen.png
Binary files differ
diff --git a/docs/ta/img/vg151_funny_shit/target_os_iphone.png b/docs/ta/img/vg151_funny_shit/target_os_iphone.png
new file mode 100644
index 0000000..9a017c6
--- /dev/null
+++ b/docs/ta/img/vg151_funny_shit/target_os_iphone.png
Binary files differ
diff --git a/docs/ta/img/vg151_lab1/lab1_dont_understand.png b/docs/ta/img/vg151_lab1/lab1_dont_understand.png
new file mode 100644
index 0000000..d5c802f
--- /dev/null
+++ b/docs/ta/img/vg151_lab1/lab1_dont_understand.png
Binary files differ
diff --git a/docs/ta/img/vg151_lab1/lab1_rc.png b/docs/ta/img/vg151_lab1/lab1_rc.png
new file mode 100644
index 0000000..e3f10b9
--- /dev/null
+++ b/docs/ta/img/vg151_lab1/lab1_rc.png
Binary files differ
diff --git a/docs/ta/index.md b/docs/ta/index.md
new file mode 100644
index 0000000..62d183d
--- /dev/null
+++ b/docs/ta/index.md
@@ -0,0 +1,42 @@
+# TA Adventures
+
+For some reason, I managed to become a TA for VG151, Accelerated
+Introduction to Computers and Programming. There are a few possible
+reasons. One of my guesses is that the professor is also a Linux user.
+
+Our course infrastructure is almost exclusively FOSS:
+
+- Video conference: Zhumu (Chinese Zoom), non-FOSS
+- SCM: git, FOSS
+- Git forge: self-hosted Gitea, FOSS
+- Instant messaging: self-hosted Mattermost, FOSS
+- Online judge: self-hosted JOJ, FOSS
+- Canvas, FOSS<sup>[citation needed]</sup>
+- MATLAB, non-FOSS
+- C/C++ Compiler: gcc, FOSS
+- Build system: GNU Make and CMake, FOSS
+
+On 2022-08-19, I received an email from the undergraduate education office
+(UEO) that I got the offer. Naturally I went "woo-hoo party timeeeee".
+Later I met my colleagues, two sophomores and one junior, plus another
+junior whose title is "volunteer assistant" (thanks Mack!).
+
+On 2022-09-13 the semester began with a campus lockdown due to a positive
+case of Covid. As a result all lectures that afternoon switched to online
+mode, just like the previous summer semester except now I'm at school
+rather than home. This sucks _ass_. This _might_ be reversed if
+circumstances go the good way, or it just might stay.
+
+Update: we switched to "hybrid mode" in late October, and then offline
+only (save for international students). I went to VG151 classroom twice,
+it was pretty populated.
+
+Update: whoops, back to online.
+
+Anyway, enjoy my compilation of blogposts on life being a TA:
+
+- [Funny shit](vg151_funny_shit.md)
+- [Lab 1](vg151_lab1.md)
+- [Exam 1](vg151_e1.md)
+
+VG151 came to an end in December 2022.
diff --git a/docs/ta/vg151_e1.md b/docs/ta/vg151_e1.md
new file mode 100644
index 0000000..18fce73
--- /dev/null
+++ b/docs/ta/vg151_e1.md
@@ -0,0 +1,455 @@
+# VG151 — Midterm 1: A Chronicle
+
+(All timezones are UTC+8)
+
+## 2022-10-19 10:00, Zoom
+
+The ENGR151 teaching team (TT for short) gathered online for our first
+exam preparation. At first we thought the exam would be online and spent
+an hour thinking of ways to prevent cheating, as there has never been an
+online coding exam. We have a few questions on their project (for example
+how did you plot your rectangles in matlab) that would be super easy to
+cheat on, so Manuel proposed an oral exam (as was tried on international
+students who attended this course online constantly).
+
+Fortunately, this exam model was never tried because we got news that next
+week will be offline again, after more than one month of lockdown.
+
+The exam consists of two parts: part A is on paper, and part B is coding.
+We created a Gitea issue to track our ideas for part B.
+
+## 2022-10-19 11:08, Gitea
+
+One TA submitted their idea to the thread. It's about [Sierpiński
+triangles](https://en.wikipedia.org/wiki/Sierpi%C5%84ski_triangle). I know
+where they found it: [the Wikipedia article on recursion, section "In
+Mathematics"](https://en.wikipedia.org/wiki/Recursion#In_mathematics).
+
+![Evolution of a Sierpiński triangle](img/vg151_e1/sierpinski.png)
+
+## 2022-10-19 16:46, Gitea
+
+Another idea emerged. It was about a basic line interpreter that takes
+a file of definitions of shapes (position, size, color, etc) and plots
+them accordingly.
+
+## 2022-10-19 17:47, Gitea
+
+And here's my idea: I was taking discrete math, and in set theory they
+have this trick where you represent a natural number `n + 1` with `n
+U {n}` (where `U` stands for union). So, if we take 0 to be the empty set
+`{}`, 1 is `{} U {{}} = {{}}`, 2 is `{{}} U {{{}}} = {{}, {{}}}`, etc etc.
+Here's my code:
+
+```
+function set_theory
+ n = input('Input a natural number: ');
+ disp(n2s(n));
+end
+
+function s = n2s(n)
+ % convert natural number to set theory representation
+ if n == 0
+ s = '{}';
+ return
+ elseif n == 1
+ % handle edge case where we need no comma
+ s = '{{}}';
+ return
+ end
+
+ s1 = n2s(n - 1);
+ % pretty much a hack: strip the trailing },
+ % push s1, and put the } back
+ s = [s1(1:end-1) ', ' s1, '}'];
+ return
+end
+```
+
+I thought it was a simple exercise, almost _too_ simple as long as you
+know how recursion works.
+
+## 2022-10-20 13:06, Gitea
+
+Manuel has read all our ideas, and decided to go with two:
+
+- The shape interpreter, and
+- My set theory recusive algorithm
+
+However, he commented that the interpreter needs to be more challenging,
+and that he will remove the set theory notation from mine in favor of
+something else, but it remained a mystery at the moment.
+
+## 2022-10-20 17:15, Gitea
+
+Babe wake up, new interpreter specifications just dropped
+
+We added:
+
+- `compose` instruction that works like classes
+- fill colors
+- absolute or relative movement
+
+## 2022-10-21 21:15, Mattermost
+
+Manuel put together an early draft of the testpaper for us to check. There
+are three exercises:
+
+- One exercise about the shape interpreter
+- One exercise about some alien sci-fi in the Minami-ke lore
+- One exercise about RGB image manipulation
+
+The sci-fi goes like this: (paraphrased)
+
+Minami Haruka shows her sisters a weird device she found with a keypad and
+a screen. When she presses 0, screen reads `-.`; 1 → `--..`,
+2 → `--._--...`, and 3 → `--._--.._--._--....`.
+
+Kana suspects that aliens are using this device to "communicate as they
+plan to invade campus", but Haruka found a pattern. What pattern?
+
+`-` is `{`, `.` is `}`, and `_` is `,`.
+
+Now that the lore is over, the questions are
+
+- What is 4
+- Diagram of recursion
+- Base case
+- Steps repeated
+- Describe algorithm
+- Implement algorithm
+- What is 12
+
+I admire Manuel's imagination that exploded an innocent set theory
+notation to make such a bizzare scenario.
+
+Also, another side effect is, the order of this sequence is well-defined.
+No need to worry about `{{}, {{}}}` and `{{{}}, {}}`.
+
+## 2022-10-22 19:28, Mattermost
+
+Manuel sent us his semi-ready version of the testpaper, on which
+I discovered a flaw:
+
+> Convert from hexadecimal into binary: 16, AG.
+
+I reported it to Manuel, expecting him to fix it:
+
+> pretty sure `AG` isn't a hex number
+
+Here's how he replied:
+
+> `AG` i know this is not hexadecimal, but do they? :smirk\_cat:
+
+> I like tricky questions...
+
+> when they ask us duirng [sic] the exam we just tell them if they think
+> there is a mistake they explain it
+
+At this moment I knew our students are about to be bamboozled. We'll wait
+and see…
+
+Also, there was a sample output image for the shape interpreter exercise.
+On the screen it looks like this (cropped so I don't get sued):
+
+![A color image with green, yellow, cyan, red and black
+](img/vg151_e1/graph_color.png)
+
+However, I'd imagine the paper would be printed in grayscale, which means
+it will look like this:
+
+![Grayscale of previous image; everything is either black or the same
+shade of 50% grey](img/vg151_e1/graph_hsv.png)
+
+Manuel changed the grayscale method to HSL (Hue, Saturation, Luminance) so
+it looks better:
+
+![Still grayscale but colors are distinct](img/vg151_e1/graph_hsl.png)
+
+## 2022-10-23 11:00, Mattermost
+
+A few more typos and ambiguities are fixed, but there came another
+problem: the image Manuel asked students to manipulate, `forest.tif`, is
+part of an image processing toolbox, so we switched to `corn.tif` to be
+safe.
+
+## 2022-10-23 17:19, Mattermost
+
+The exam papers are ready for printing.
+
+## 2022-10-26 13:18, Gitea
+
+I created e1 repos for everyone and drafted an announcement for testtakers
+to clone it.
+
+## 2022-10-27 13:50, UEO
+
+Menako got the exam papers in a bag from UEO.
+
+## 2022-10-27 15:45, exam room
+
+We had 85 students — 78 of which took the exam offline. We had 44 in our
+exam room, the rest in another.
+
+We unsealed the package of testpapers, and found this thing:
+
+![Grayscale image, but now the sun is also black and fused into the
+road](img/vg151_e1/graph_printed.png)
+
+As a fix, when part B began we would project the image we intended on the
+screen.
+
+## 2022-10-27 16:00, exam room
+
+The bell struck and the exam commenced. Little did they know, they were in
+for a big surprise. Hands in the room I proctored rose in confusion.
+"Excuse me," one asked, "this question doesn't look quite right to me."
+
+I asked, "what's the problem with it?"
+
+They replied, "I don't think `G` is a—"
+
+"Just write down what you think. Manuel's probably thinking the same
+thing."
+
+Participatory exam: an exam where students are not the only ones taking
+it; instead, proctors play a major part in maintaining the effect of trick
+questions. We indeed tried very hard not to spoil it.
+
+It's official folks, exam proctoring is a performance art now.
+
+## 2022-10-27 16:30, exam room
+
+Time for part B. I walked around, and was amazed by the multitude of
+laptop models in the modern age. I guess half-tablet half-keyboard C-sides
+are a thing now?
+
+At least three testtakers got confused halfway through exercise 3, waved
+at me and asked "what's the corn image?" I shrugged and told them to read
+on.
+
+## 2022-10-27 17:35, exam room
+
+One testtaker raised a question:
+
+In exercise 1 (shape interpreter) we provided some sample code to be
+interpreted. One line goes
+
+```
+square background cyan (0,0) 20 20
+```
+
+Another goes
+
+```
+square road black (0,-5) 10
+```
+
+Obviously they are off by one parameter. Turns out the specification and
+the second line were changed, but the first one wasn't. We ended up
+issuing an erratum 10 minutes before the exam ends.
+
+## 2022-10-27 17:45, exam room
+
+It's time to submit everything to Gitea. I had a flash drive in case
+someone's antenna melts, but it turned out unnecessary. The process was
+simple as `git add ./; git commit -m 'e1'; git push` then opening
+a release called `e1` on Gitea.
+
+We collected and counted the exam papers, then dismissed the exam.
+
+## 2022-10-27 17:57, exam room
+
+All e1 repos are archived. As our room was mostly empty, we went ahead to
+the other room which Manuel was a proctor of.
+
+I assigned myself to exercise 2 of part B, because that's the way karma
+works; I contributed the idea after all. So did the TA who made exercise 1.
+Menako assigned herself to most of Part A, and expected to begin grading
+that night.
+
+Discussion rooms in Longbin Building, the official building of Joint
+Institute, were not open for booking, so we had to use Manuel's office. He
+handed Menako his key. It was the rarest of in-game items, uncraftable by
+any means.
+
+## 2022-10-27 18:37, McDonald's
+
+The rest of the TA group had plans that night so Menako and I had burgers
+at McD's, then rode off to Longbin Bldg.
+
+## 2022-10-27 19:00 or something, Manuel's office
+
+Manuel's office is tiny. Crappy monitor, crappy keyboard, but many chairs
+and a bookshelf of math textbooks.
+
+Exercise 2 was the only one in part B to require answers on paper or the
+README file, so the first thing I did was to classify the answer sheets
+based on where they wrote them. That night I graded everything on paper,
+but wasn't able to read all the README files.
+
+Menako, on the other hand, graded part A with blazing efficiency. What
+quickly grabbed our attention was how she should grade the `AG` question.
+After some discussion, we agreed upon this rubric:
+
+- If you think `G` is 16 (or any other number), you get zero points.
+- If you leave this question blank, you get full marks.
+- If you explain that `G` is not a hexadecimal digit, you get full marks
+ plus a star (no points, but it's a token of prestige).
+
+She finished her part that night.
+
+## 2022-10-27 23:00 or something, [undisclosed location]
+
+I need to come up with a way to grade the last question (what is 12)
+without going through the trouble to scan through all 10,239 characters
+(that's how long it is). And the solution? Checksum.
+
+I grep'd everyone's README.md for >10k-character long sequences of `-_.`,
+piped them to md5sum, then sorted them. The command is
+
+```
+$ grep -hoE '[-_\.]{10000,}' FILE.md | md5sum
+```
+
+There were 7 different versions:
+
+- One is empty;
+- One is correct;
+- One is correct if you strip one dot (they wrote it in a markdown ordered
+ list) from the beginning;
+- One is truncated at 10k characters because they copied it from the
+ variable window instead of command window. No point for you, sorry.
+- The other three are incorrect.
+
+Subsequently I spent a dozen minutes committing the grades with a red pen
+on paper. As this question is worth one point, I just treated it as
+atomic.
+
+That night I also graded all the README's.
+
+## 2022-10-28 12:55, Manuel's office
+
+The next day I went back to Manuel's office again, this time to get things
+done for once and for all. All my colleagues came along (except Menako).
+The desk was crowded but fortunately I'm mostly done with paper, so I took
+a corner to grade all the code.
+
+My rubrics are based on four criteria:
+
+- Base case (2pt)
+- Recursive case (3pt)
+- Coding style (1pt)
+- Your code doesn't crash (1pt)
+
+Yes, if your code is unindented or looks like a mess, I will take away one
+point.
+
+And there are two special rules:
+
+- Iterative algorithms get no point
+- Marginally recursive algorithms get 4pt max.
+
+What counts as marginally recursive? Well, consider this Python snippet:
+
+```
+def alien(number, string, target):
+ if number == target:
+ return string
+ string = string[:-1] + "_" + string + "."
+ return alien(number + 1, string, target)
+
+print(alien(1, "--..", 3))
+```
+
+Does it work? Yes. Does it yield the correct result? Yes! Is it recursive?
+
+…Maybe?
+
+I mean, it *does* call itself. But it's not what we meant! Observe how
+`number` grows larger and `string` longer as we go to the deeper level.
+Recursion as we taught in the lectures was the opposite: breaking down the
+problem until it falls within the base case(s). Furthermore, this is tail
+recursion, which is just iteration with extra steps.
+
+
+## 2022-10-28 16:42, Manuel's office
+
+Part A is finished for all paper submissions.
+
+## 2022-10-28 17:09, Manuel's office
+
+Part A is finished for international students also.
+
+## 2022-10-28 20:21, Manuel's office
+
+Grading finished for the whole exam.
+
+## 2022-10-28 20:34, Mattermost
+
+We begin discussing curves. Manuel remarked:
+
+> no rush to publish grade. you graded too fast!!
+
+and
+
+> fastest 101/151 grading ever!!! :partying_face:
+
+We decide to delay the release, at least not until the next weekday.
+
+## 2022-10-29 10:02, Mattermost
+
+Remember on 2022-10-23 we switched to `corn.tif` for Part B ex3 "to be
+safe"? Huge mistake. It turned out, `corn.tif` wasn't an RGB image as we
+thought it was — it was an _indexed_ image, which means it comes in two
+parts:
+
+- A list of key-value pairs of color indexes and RGB values
+- A matrix of indexes
+
+To convert it to RGB, you need to call `ind2rgb`. At first this was part
+of our rubric, but after a while we found it too harsh, so we just awarded
+points to whoever processed it correctly as if it were RGB.
+
+## 2022-11-01 09:00, Canvas
+
+Grades are released. Paper checking is scheduled at 2022-11-03
+20:20-22:20.
+
+## 2022-11-03 20:00, meeting room
+
+I arrived at the meeting room reserved for the paper checking session and
+laid out the testpapers. A few eager students were waiting outside.
+
+As time went on, the room became increasingly crowded. A line appeared by
+my side, asking for points.
+
+The single most asked question is:
+
+> Why did I get zero for "write an algorithm"? I wrote it in my code!
+
+For context, we have two contiguous subquestions:
+
+- Write an algorithm describing how the conversion works
+- Implement the algorithm in matlab
+
+Both were assigned 7 points, so you see the importance of the former. What
+I was expecting was:
+
+- A piece of text
+- Pseudocode
+- Comments in code
+
+Sadly, many assumed "my code is self-documenting, it answers both
+questions." Sorry, you're misinterpreting what we meant by an algorithm.
+
+There was one case though, I forgot to check someone's README file
+somehow, and wasn't aware they wrote pseudocode. I read it, and it made
+all the sense I wanted to see. Ka-ching, 7 points. The same person had
+their Part B ex1 grade bumped by 6 points (grade looked like 10, but was
+actually 16). That's 13 points in total. _So lucky._
+
+## That's a wrap!
+
+This is everything that (a) I feel like sharing and (b) I am allowed to
+share.
diff --git a/docs/ta/vg151_funny_shit.md b/docs/ta/vg151_funny_shit.md
new file mode 100644
index 0000000..f2d510a
--- /dev/null
+++ b/docs/ta/vg151_funny_shit.md
@@ -0,0 +1,285 @@
+# VG151 — Funny Shit
+
+Being a TA is mostly pain, but occasionally you get comedy as a side
+effect: newbie questions that make no sense, hilarious program behavior,
+creative approaches to a problem, you name it. Every time I see one, I'll
+post it here.
+
+All screenshots and conversations are anonymized. If you see yourself in
+this blogpost, please don't take it personally. These funny stories are
+TAs' primary source of amusement. Please let us have them. Oh, and the
+"shit" in this post's title is by no means derogatory.
+
+If you feel extremely uncomfortable with any entry below, please send
+email to `<fkfd at fkfd dot me>`.
+
+## 2022-09-13, Lab 0
+
+![They are adding an SSH key but the key they entered is "ssh-keygen -t
+ed25519 -C example@example.com"](img/vg151_funny_shit/ssh-keygen.png)
+
+This is a reenactment of a screenshot they sent me to troubleshoot.
+
+## 2022-09-15, group chat
+
+Someone asked if the compiler they need to install was VSCode. One TA
+corrected them that VSCode is an IDE. Another TA corrected that TA because
+VSCode is technically a text editor, and Visual Studio is an example of an
+IDE.
+
+_slow down we'll be getting there_
+
+## 2022-09-22, TA group chat
+
+One of our TAs decided to get a Moss account. The
+[website](http://theory.stanford.edu/~aiken/moss/) says:
+
+![](img/vg151_funny_shit/moss_example.png)
+
+<details>
+<summary>Transcript</summary>
+<p>To obtain a Moss account, send a mail message to moss@moss.stanford.edu.
+The body of the message should appear exactly as follows:</p>
+<p>registeruser<br/>
+mail <i>username@domain</i></p>
+<p>where the last bit in italics is your email address.</p>
+</details>
+
+Well, here my colleague goes (reenactment):
+
+![](img/vg151_funny_shit/moss_reenactment.png)
+
+<details>
+<summary>Transcript</summary>
+To: "moss@moss.stanford.edu"<br/>
+Subject: Moss Register<br/>
+registeruser<br/>
+mail <i>their.email@sjtu.edu.cn</i>
+</details>
+
+Minutes passed and no reply. Another TA told them they should compose in
+plaintext and that italics were unnecessary. Stanford lied; turns out the
+message _doesn't_ "appear exactly as follows" after all.
+
+## 2022-09-24, Gitea
+
+![User with Jiang Zemin avatar closed a PR](img/vg151_funny_shit/jiang_avi.png)
+
+AH, nothing like seeing a user on gitea with a pfp of a distinguished
+alumnus of ours.
+
+## 2022-09-25, DM
+
+Paraphrased:
+
+> Will you teach us how to finish the project step by step?
+
+My reply was:
+
+> of course
+
+and 10 seconds later:
+
+> not
+
+## 2022-09-27, Lab 2
+
+![Zoom chatbox. Message reads "TA I needed to go for a nuclear acid
+test"](img/vg151_funny_shit/nuclear_acid.png)
+
+P… pretty sure it's called a nucleic acid test? \*Ground rumbles\* \*Hears
+the word "Chernobyl" in the distance\*
+
+## 2022-09-27, teaching team channel
+
+One student reached out to ask this question (paraphrased):
+
+> Can I use other languages than MATLAB for project 1?
+
+Their rationale is, they thought the project description document we
+handed them didn't specify which language (spoiler: it did), so they
+assumed they could use any.
+
+Well _in theory_ you could write the project in Python with matplotlib. Or
+in C/C++ with OpenGL. But… better not talk about it.
+
+(See followup below)
+
+## 2022-09-30, Gitea
+
+On 16:54 it came to my attention while `git pull`ing all the students'
+homework repos that one branch was 108.26MiB in size. When uncompressed,
+it grew to 831.3MiB. It should not be larger than 1MiB.
+
+I smell fuckup fumes from a distance away. Upon close examination, they
+pushed their project code, which should have been strictly kept personal.
+Worse, there are two versions. Worse, neither is in Matlab, but in C++.
+Worse, one of them is built on Qt. Worse, they were written with Visual
+Studio. Worse, they pushed all the vsidx, vcxproj, ipch and all kinds of
+crap.
+
+Manuel predicted that students will do all sorts of stupid things, going
+as far as considering git lfs. Real conversation:
+
+> TA: will lfs be used in this course?
+
+> Manuel: it should not but we never know what they will push by mistake…
+
+Murphy's law worked. Apart from this VS atrocity, students were pushing:
+
+- tarballs / zip files they should be submitting to the OJ
+- batch file automating tar
+- pdf
+- symlinks to the pdf
+- a CC BY 4.0 International license
+
+## 2022-10-01 to 2022-10-02, Canvas (and everywhere else)
+
+Right past midnight, as the calendar flips to 2022-10-01, I began grading
+all freshmen's homework 1 with a Python script inherited from past TAs.
+The script got a few hiccups, because:
+
+- I misconfigured the # number of OJ test cases
+- OJ server constantly crashes
+- Students' first name on Gitea is capitalized but not on Canvas
+- Off-by-one bug produces non-existent group, hgroup-00
+
+It took me more than an hour to fix them. At 01:47, I finally released
+freshmen's first homework grade. It was disastrous. By tradition we gave
+them one day to fix mistakes they've made, and the next midnight I re-ran
+the script. Signs of improvement are showing, but there still remains four
+groups who got a full deduction (-2.5).
+
+Naturally, my inbox was flooded with complaints and questions (around 25),
+especially from these four groups. When I woke up, I decided to
+investigate.
+
+- Group A: Named their README file `README .md`
+- Group B: Named their README file `README(ex1).md`
+- Group C: Pushed to master but forgot to release
+- Group D: Not enough teammates to approve PR
+
+Considering A and B made understandable freshman mistakes, C had
+a technical problem as explained by team member, and D actually did
+contact us beforehand, I negotiated a partial refund with Manuel.
+
+## 2022-10-08, JOJ
+
+While a fellow TA was developing a JOJ integration plugin for VSCode, they
+discovered that they could submit Python code to a Matlab exercise, and,
+better yet, pass all test cases by mimicking the "MATLAB is selecting
+SOFTWARE OPENGL rendering" splash text.
+
+They said it only works if the file is named `foo.py` and uploaded via
+handcrafted HTTP POST requests, not the website.
+
+The maintainer of JOJ claims, allegedly, that they had zero fucking idea
+about it.
+
+## 2022-10-11, Mattermost
+
+We have an integration that posts all PRs from Gitea to MM. However, there
+are two problems with text rendering:
+
+- HTML is not unescaped inside `<code>`
+- Emoticons are converted to emoji, even when it shouldn't be one
+
+Enjoy whatever this is:
+
+![](img/vg151_funny_shit/mm_emoticon.png)
+
+<details markdown="1">
+<summary>Transcript</summary>
+FOCS Gitea (BOT) 20:16
+
+\[ENGR151-22/hgroup-REDACTED\] Pull request opened: #14 h3-REDACTED by REDACTED
+
+ #14 h3-REDACTED
+
+\# Homework ❤\>
+
+This part is only for indivudal submission. Delete it for the group submission.
+Replace all elements between `&lt; &gt;` by appropriate values. To tick a box
+change `- [ ]` into `- [x]`.
+
+\## Overview
+
+Exercises completed: <1,2,3,4,5,6>
+</details>
+
+## 2022-11-09, East Middle Hall
+
+A freshman asked me to arrange an office hour for their project 2, so
+I did. Context: project 2 is an Uno spinoff but with regular poker cards.
+
+I checked their code. First thing I saw was `struct Puke`. I asked them
+what it was. It was pinyin transliteration for "Poker". I tried to explain
+"Puke" means "to vomit" in English. They didn't care. 🤷
+
+## 2022-11-19, some conference room
+
+We were about to deliver a recitation class for the midterm, and my
+colleague put on Minami-ke music (in case you didn't know, the three
+sisters from [Minami-ke](https://en.wikipedia.org/wiki/Minami-ke) are
+recurring characters in the VG151 lore.
+
+![Playlist titled "Minami-ke OP/ED"](img/vg151_funny_shit/minamike.png)
+
+## 2022-12-15, Mattermost
+
+We were about to hold the final exam the next day, and since it was
+online, we decided that Canvas quizzes are the way to go. I clicked into
+one they were working on, clicked Edit then Cancel, but a heads-up told me
+the quiz was deleted. I was like "I done fucked up". Quickly I admitted my
+mistake to my colleagues.
+
+![Chat dialogue](img/vg151_funny_shit/deleted_quiz.png)
+
+<details markdown="1">
+<summary>Transcript</summary>
+Me: @TA-redacted um, this is gonna be really awkward, but I accidentally
+deleted the quiz
+
+Me: idk how it happened all i did was poke around
+
+TA-redacted: no, i [sic] deletes it
+
+Me: bruh
+
+(there was a ROFL emoji reaction below my first message.)
+</details>
+
+## 2022-12-16, dorm
+
+I was grading project 2 late at night and saw this monstrosity.
+
+![Screenful of code, hardly any linebreaks](img/vg151_funny_shit/p2_code.png)
+
+clang-format to the rescue.
+
+## 2022-12-18, dorm
+
+Still grading project 2, but this one made me chuckle.
+
+![Part of a C source file](img/vg151_funny_shit/target_os_iphone.png)
+
+<details markdown="1">
+<summary>Transcript</summary>
+
+```
+//----------------Apple OS-------------
+#elif __APPLE__
+
+#if defined(TARGET_OS_OSX)
+// MacOS
+
+#elif TARGET_OS_IPHONE
+// IOS
+
+#else
+// other APPLE os
+#endif
+```
+</details>
+
+For context, our project was meant to run on computers in the terminal.
diff --git a/docs/ta/vg151_lab1.md b/docs/ta/vg151_lab1.md
new file mode 100644
index 0000000..45698df
--- /dev/null
+++ b/docs/ta/vg151_lab1.md
@@ -0,0 +1,142 @@
+# VG151 — Lab 1
+
+2022-09-21
+
+Last night I held my very first real lab this semester, lab 1.
+
+The single worst thing is, it had to be online. Fall semester last year
+was offline, but this one only lasted four hours before campus imposed
+a lockdown and forced us online. I cannot overstate how disruptive this
+is. I was about to speak shit of some people but my lawyer advised me not
+to.
+
+## Gather All Students Challenge (Impossible)
+
+Before the lab I needed to ensure everyone was on our self-hosted Gitea
+and Mattermost in the department server. They were not. It simply was not
+possible to get everyone to follow steps as simple as clicking an invite
+link, even if we four TAs together held three lab 0 sessions last week to
+help them sign up on each of them. We even sent a Canvas annoucement.
+Guess how many didn't listen?
+
+5 out of 83.
+
+Yeah, it's natural to forget things in the first days of university,
+especially in such turbulent times. But guess how many remain unreachable
+even after we sent them a personal email?
+
+2.
+
+_Well guess you don't care about this course then_
+
+As a result, at the beginning of the lab, several homework groups were
+lacking a member. There are supposed to be four. One even lacked two.
+
+## Attendance and Atmosphere
+
+Attendance was not great. I expected 27 students; 15 showed up on time. At
+the peak there were about 20.
+
+I tried using sketches and puns to cheer up the atmosphere, but you can't
+really expect anything online. I was virtually talking into a vacuum, but
+I don't mind. I am not paid enough to.
+
+## Git, Gitea, and Getting everyone confused
+
+Online labs are just _amazing_. Nothing feels better than helping
+a clueless student to figure out what an error message means. Via Zoom. In
+the chatbox. Where a message takes a dozen seconds to write and another
+dozen to read. Just _fantastic_.
+
+I asked every attendee to set up their git repos and follow a tutorial
+that past TAs wrote. It's about submitting a hypothetical homework, h0.
+I spent half an hour demonstrating, then gave them another half hour to
+follow it. The git part involves:
+
+- `git clone`
+- `git checkout`
+- `git add`
+- `git commit`
+- `git push`
+
+And gitea:
+
+- Opening PRs
+- Assigning teammates
+- Reviewing PRs
+- Merging PRs
+- Releasing
+
+Rudimentary, yes, but most of who I'm talking to were completely fresh to
+git. So many students had zero idea what I was doing, let alone what they
+should do.
+
+![Zoom chatbox. An attendee says "Ummmm.....I don't understand what we are
+doing from the beginning to now..." and five more say "+1"](img/vg151_lab1/lab1_dont_understand.png)
+
+Unfortunately this was the best I could do, so all I could say is, you
+gotta follow the tutorial I sent you.
+
+Fresh students often struggle to understand how placeholders work. For
+example, one student asked me why they got an error on this step:
+
+```
+git checkout -b <your_student_id>
+```
+
+I checked and sure enough, they thought these angle brackets were literal
+ones. Fortunately this sort of misunderstanding usually happens only once.
+
+Update: another student did the same on the Wednesday section.
+
+While each group was trying out git and gitea on their own, I closely
+inspected all their repos on gitea. A common, obvious mistake is that
+some of them were using arbitrary strings as branch names, whereas it says
+"your student id" in the tutorial. This was my fault; I used alice and bob
+in my demonstration for "simplicity", but quite the contrary, it misled
+students.
+
+I was in charge of 7 groups. At the end, two were very close from
+completing everything; others had varying levels of success. Many
+individuals hardly did anything at all. (They _might_ be doing things
+locally, but come on.)
+
+## Rule 0 of online labs: no one will speak
+
+Absolutely no one unmuted their microphone. I believe I was the only one
+that ever spoke. Kinda expected that, huh?
+
+Anyway, I survived an awkward interaction via chatbox, and ended the lab
+leaving most attendees still confused.
+
+I also made a mistake when explaing the difference between `A'` and `A.'`
+in matlab. I thought they both transpose `A` because I am an inferior
+being constrained to the real number line, unaware of the complex plane.
+Apparently `A'` is the complex conjugate and `A.'` is the real
+transposition.
+
+## Acknowledgement
+
+At the end of the lab I got this one PM though:
+
+![Zoom chatbox. A student PMs me "Really helpful.Thanks for the RC"](img/vg151_lab1/lab1_rc.png)
+
+Technically this isn't an RC, but I'll take it. Thanks!
+
+I would like to thank Manuel, Mack, Menako, King and Fly for their support
+in my first lab. I would also like to thank
+[Boming](https://boyanzh.site/) for Joint-Teapot, an automation tool that
+helped me with chores that would otherwise have to be done manually 21
+times.
+
+## Conclusion
+
+Lessons learned:
+
+- If students will use a document, keep it on the side of my screen while
+ I demonstrate anything and constantly remind them of where I am
+- If I need to demonstrate a gigantic heap of steps, I'd better partition
+ them into ingestible chunks
+- I should probably go harder on the students and force some of them to
+ show their work
+- Do not simplify things too far
diff --git a/docs/umich/f23_winter_break.md b/docs/umich/f23_winter_break.md
new file mode 100644
index 0000000..f02fbf3
--- /dev/null
+++ b/docs/umich/f23_winter_break.md
@@ -0,0 +1,98 @@
+# Fall 2023 winter break wrapup
+
+2024-01-11
+
+This winter break I did a few things.
+
+## Things I cooked
+
+- Made potato soup with mushrooms and broccoli
+
+<details markdown="1">
+<summary>Three attempts</summary>
+
+![Potato soup with mushrooms](img/f23_winter_break/mushroom_soup.jpg)
+
+▲ First attempt with mushrooms
+
+![Gooey mixture of potato and
+broccoli](img/f23_winter_break/broccoli_goo.jpg)
+
+▲ This hardly looks like soup
+
+![Potato soup with broccoli](img/f23_winter_break/broccoli_soup.jpg)
+
+▲ Refined attempt
+</details>
+
+Note: I imitated the "cream of mushroom/broccoli soup" in dining halls but
+mine was less creamy, probably because I used milk instead of cream.
+I made it like four times in giant portions until I got sick of it and
+just cooked pasta.
+
+- Tried mixing corn with flour and frying a crispy "corn cake" but just
+ resulted in very greasy corn (also caused kitchen sewage pipe to break
+ because I poured hot oil down there)
+
+![PVC pipe under garbage disposal breaking at a 90 degree
+connector](img/f23_winter_break/broken_pipe.jpg)
+
+- Roasted tomatoes, carrots, and bell pepper, as well as leftover potatoes
+ and mushrooms I couldn't cook because the pipe broke
+
+## Things I learned
+
+- How to ice skate
+- Rust
+- How the `fork` and `dup2` syscalls work
+- How to open a pipe (POSIX, not physical)
+
+## Things I saw
+
+- A bunny
+
+![Grey bunny chilling in the grass](img/f23_winter_break/bunny.jpg)
+
+- Steamboat Willie
+- Tom Scott's goodbye video
+
+## Things I gained
+
+- FCC ham radio license
+
+I talked with a person over the W8UM repeater and later participated in
+the net. I was nervous as fuck but handled just fine
+
+- A free eggs coupon, when I came back home with a carton of eggs I just
+ bought at full price
+- A jug of tea for $1 despite the $3 price because the barcode was missing
+
+## Things I lost
+
+None to my knowledge so far.
+
+## Things I played
+
+- [Shenzhen I/O](../random/shenzhen_io_review.md)
+- Bass
+
+## Things I read
+
+- Lakoff, George. _Women, Fire, and Dangerous Things: What Categories
+ Reveal about the Mind._ U of Chicago Press, 1987. Probably chapter 8-14
+
+## Things I wrote
+
+- [hamrs](https://git.sr.ht/~fkfd/hamrs)
+- [shenzhen_solitaire](https://git.sr.ht/~fkfd/shenzhen_solitaire)
+- A couple blogposts (one)
+
+## Conclusion
+
+I fulfilled a few life goals this winter (e.g. ham radio license). While
+some friends did, I never went out of town. Mostly I stayed at home and
+only took the bus to the supermarket or to the ice rink. I did absolutely
+nothing festive on Christmas or the new year. However, when winter break
+began, I cracked open a soda because I am not yet at drinking age.
+
+__Overall rating: 75/100__
diff --git a/docs/umich/f23_wrapup.md b/docs/umich/f23_wrapup.md
new file mode 100644
index 0000000..c1cd6b1
--- /dev/null
+++ b/docs/umich/f23_wrapup.md
@@ -0,0 +1,456 @@
+# Fall 2023 wrapup
+
+2023-12-18
+
+My first semester at UMich is over, so here's my review.
+
+## Course review
+
+### EECS 370
+
+Rating: __4.5 / 5__
+
+Theme: computer organization and (micro)architecture
+
+#### Instructor (Mark Brehob)
+
+Pros:
+
+- Tons of experience, both industrial and academic
+- Knows everything
+- Is furry-inclusive
+
+Cons:
+
+- Talks too fast
+
+#### Things I learned
+
+- How assembly is written
+- How assembly is executed
+- Assembler & linker
+- Processors
+ - Single-cycle
+ - Multi-cycle
+ - Pipeline
+- Cache
+- Virtual memory
+
+I had a headstart with the single-cycle processor thanks to
+[nand2tetris](../projects/nand2tetris_1.md) but the pipeline really blew
+up my head. It explains why modern computers are so fast while not running
+into timing errors all the time.
+
+Cache and virtual memory, however, are just book-keeping, and make up for
+the most boring exam questions. Virtual memory (in the scope of 370)
+involves a lot of handwaving. Like, I know what page tables look like. But
+who's managing it? Is the TLB part of the ISA? If I wanted to write
+a kernel how much work is on my side? So many questions that hopefully
+will be resolved in 482 (operating systems).
+
+#### Projects
+
+All projects are written in C.
+
+- p1
+ - p1a: assembler (LC2K → binary)
+ - p1s: simulator (simulates binary)
+ - p1m: multiplication (write a program that calculates 6203×1429 in
+ assembly)
+- p2
+ - p2a: assembler (LC2K → object file)
+ - p2l: linker (object files → binary)
+- p3: pipeline simulator
+- p4: cache simulator
+
+Some argue p2l is the hardest part, but that isn't the case for me. I find
+p3 the most difficult in that (a) there's a lot of data shuffled around in
+the pipeline and (b) it's the only project I went to an office hour for.
+
+#### Favorite moments
+
+- When Mark came in wearing a pointy hat he said was for "ease of
+ identification in office hours"
+- When four people showed up in their fursuits on Halloween and Mark was
+ like "hey cool can I take a photo afterwards"
+
+### EECS 281
+
+Rating: __3.5 / 5__
+
+Theme: data structures and algorithms
+
+#### Instructor (Marcus Darden)
+
+Pros:
+
+- Pretty good at explaining things
+- Has a "lecture" playlist that he plays before lecture
+- You _have_ to see how long his hair is
+
+Cons:
+
+- Sometimes slow
+
+#### Things I learned
+
+- How to use C++ STL efficiently
+- Big O notation
+- Common data structures and how they manage memory
+ - Vectors & deques
+ - Binary heaps
+ - Hash tables
+ - Graphs
+- Algorithms
+ - Sorts
+ - Backtracking & branch-and-bound
+ - Dynamic programming
+
+VG151 (intro to computer programming) made me hate C++, because what the
+fuck is polymorphism anyway. EECS 280 made me not hate it, and 281 made me
+kind of like it (for the job it is designed for). In the four projects
+I hardly ever inherited a class, or managed memory with `new` and
+`delete`.
+
+My favorite topics are hash tables and dynamic programming. Imagine this:
+you've been forced to do your work on those pathetic
+flip-out-of-the-armrest desks, and suddenly someone gives you a huge
+table. This is what it feels to be given O(n²) space when all you've got
+so far is O(n), or worse, O(1).
+
+Fun fact: Before I took this course, I did not believe hash tables worked
+like this. Like, seriously, why are you leaving more than half the buckets
+empty??
+
+#### Projects
+
+- p1: puzzle solver with DFS (stack) and BFS (queue)
+- p2: priority queue
+ - p2a: shooting zombies (with `std::priority_queue`)
+ - p2b: implementing your own priority queue
+- p3: bank simulator (`std::unordered_map`)
+- p4: graphs
+ - part a: maximum spanning tree with Prim's algorithm
+ - part b: fast TSP (traveling salesman problem) at O(n²)
+ - part c: optimal TSP at O(n!)
+
+Note: p4b does not require a certain algorithm and is the only open-ended
+"optimization", graded on how close your result is to the instructors'.
+The instructors don't have an optimal answer either, because that would be
+O(n!) and take forever.
+
+My favorite project is p3, and it is the only project that I got 100/100.
+Reason: it's the only project that is something you'd use in real life
+applications. Managing a database sort of stuff.
+
+Project 4 is easier for me than p1 and p2 actually. There's a lot of
+nitty-bitty in the latter two. Like output formats and ambiguous
+specification. My solution leaves room for improvement, but the 3% or 4%
+deduction barely justifies the extra work, so I didn't even bother. If the
+projects were better I'd give the course a 4 / 5.
+
+#### Favorite moment
+
+- When I wrote a Tampermonkey script that plays Bad Apple!! on the
+ autograder, shared a video on Piazza, and got 28 "good note"s
+
+![Piazza note titled "why my autograder playing bad apple" with a video of
+Bad Apple!! playing on a 20×20 table, in cells colored red and
+green](img/f23_wrapup/badapple.png)
+
+### GERMAN 103
+
+Rating: __5 / 5__
+
+Theme: elementary German
+
+GERMAN 103 is 101 and 102 condensed in one semester, and so it is
+_f&nbsp;a&nbsp;s&nbsp;t_. That didn't stop me from slaying it though.
+
+#### Instructor (Laura Okkema)
+
+Absolute legend. If you disregard her competence at teaching German (which
+she has a lot of), she:
+
+- is super patient and responsible
+- uses postcards, her paintings, or her son's toys to divide class in
+ teams
+- had a dog and a cat when semester began
+- saw a dog abandoned on the street and went "oh I'll take her"
+- now has two dogs and a cat
+- enjoys melodic death metal
+- gave us precious feedback regarding profanity usage in German (see [§
+ Rollenspiel](#rollenspiel-roleplay)
+
+The only negative things I have to say about this course:
+
+- We had to write an essay (~10 sentences long) and memorize it for every
+ test, which is in my opinion not a productive way to design a test
+- Feedback on Aufsätze is often as slow as a week (it's useful though)
+- The textbook is hella expensive and MindTap has a pretty bad user
+ experience
+
+#### Assignments
+
+- MindTap (online homework thing which I forgot more than once)
+- Arbeitsblätter (worksheets for each chapter)
+- 3 Aufsätze (essays)
+- Wiederholungsblätter (test reviews)
+- Summary of the book »Oh, wie schön ist Panama« by Janosch
+
+The Aufsätze is one of the only things where I have total creative
+freedom, so I abused this privilege. In the first two I wrote about:
+
+- Das Beamermännchen (little man named Greg who lives in a projector)
+- Die Hexe (a witch named Melissa who cast a storm-repelling spell on
+ a flight)
+
+My instructor liked the first one so much she printed it out and hung it
+on the bulletin board :)
+
+#### Rollenspiel (roleplay)
+
+This section is primarily for my own archival purposes. It is not intended
+for a general audience, but it's not top secret either.
+
+tl;dr: My team performed a roleplay and won $200
+
+<details markdown="1">
+<summary>Warning: MASSIVE infodump</summary>
+
+The third Aufsatz is a group project, intended to be played out on stage.
+I knew immediately what I wanted.
+
+In the book »Oh wie schön«, on their way to Panama, the bear and the tiger
+meet a fox, who "wanted to celebrate his birthday with a goose". (Janosch
+18) Innocent as this sounds, an illustration shows the goose lying in the
+fox's lap, next to a pot and silverware. The popular belief is that the
+goose is the fox's dinner. (Myllynen 26)
+
+I refuse to believe it, so in my headcanon the fox is _not_ going to eat
+the goose. Instead he and the goose are a married couple, having an
+argument. They verbally abuse each other in front of the tiger and bear,
+triggering a dispute between the latter two (who, in my headcanon, are
+a gay couple).
+
+I pitched my idea to my groupmates, and we went with it. The problem was
+I had no way to end it, but Ava suggested we could give life to the
+Tigerente (tiger-duck, which the tiger carries with him all the time like
+a baby but doesn't talk) and let him settle the argument.
+
+Now, with a beginning and an end, all we need to do is find a trivial
+dispute that could tear not one but two relationships apart. There is zero
+description of the fox and goose's life, but there was a lot about the
+tiger and bear. The bear goes fishing, and the tiger picks mushrooms. At
+the end of the day the bear for both of them. The trivial dispute is then:
+
+- Tiger is sick of eating fish and mushrooms every day
+- Bear is the one who cooks
+
+Extrapolate this a bit, and we came up with
+
+- Goose is sick of eating lettuce and potatoes every day
+- Fox is the one who cooks
+
+I believe this is very — almost too realistic. I'm sure food related
+breakups happen routinely. What makes this even better is the fact that it
+was the fox's birthday, which makes eating the same kind of food more
+ridiculous.
+
+However, we put a fundamental difference between the two duos as well:
+
+- Bear and tiger are traveling to Panama
+- Fox and goose have stayed home for 20 years, despite fox's promise on
+ their wedding to travel around the world
+
+Which ends in the latter pair joining bear and tiger on their way to
+Panama.
+
+Now that we have the plot, we churned out a script. We made sure to
+sprinkle in two types of humor:
+
+- Situational, such as the goose saying "I have no hands, I have wings"
+ when asked why she never cooks";
+- Profanity.
+
+Yes, vulgarity is how you want the audience to engage. The most famous
+German profanity — or perhaps word in general — is Scheiße (shit), but we
+didn't end up using it. (This was, however, the first word Laura heard as
+she entered the room we were rehearsing in, the moment I knocked over
+a soap dispenser.)
+
+The profanity we used were "Wichser" (wanker) and "Hundesohn" (son of
+a bitch, or dog literally, which is anatomically not off base because
+foxes are somewhat dog-like).
+
+One day on the lecture I asked Laura if it was OK to use these two words.
+You should see her face as I threw this question. She went through a phase
+of "seriously?" and then affirmed that yes, she's OK with it.
+
+Later, as she wrote feedback for our first draft, she commented on our use
+of the two words again. She thinks we should reconsider these words, not
+because they're naughty, but because she has better alternatives.
+
+She argues that Wichser is mostly used among teenage boys, and Hundesohn
+is kind of outdated, and used rarely in Germany. In addition, she left us
+a paper that discusses animal insults. Like a whole 13 page paper. I have
+a theory that, she downloaded this paper long ago and waited for this day.
+
+She recommends the insult "dumme Gans" (dumb goose), but unfortunately the
+insult was actually directed toward the fox, and to call an animal a "dumb
+$animal" is like calling a human "dumb human", and weaken the insult.
+
+We ended up keeping Wichser and Hundesohn, because incidentally bear and
+tiger are somewhat teenagers, and the fox is "old" according to the book.
+Also, these are pretty famous insults too. This seems to convince Laura.
+
+We also learned that the best team (or two) would be nominated into
+a competition with the other German classes, and the winning team would
+receive $200 in reward. That said, I believe we had a pretty good chance.
+
+Once we had the final script, we went on to do our first rehearsal. Thomas
+caught covid and had to join us over Zoom.
+
+At the same time, I was working on the props. My inner child/engineer
+hybrid made this masterpiece:
+
+![Tigerente made out of cardboard](img/f23_wrapup/tigerente.jpg)
+
+We had one more session of rehearsal before our class performance. We
+couldn't afford full costumes, so we just improvised as best as we could:
+
+- Ava (goose) brought a feather scarf or something
+- Thomas (bear) wore a brown jacket
+- I (tiger) wore a yellow T-shirt
+
+Ryan also brought a butter knife. The guidelines forbid weapons, so it was
+plastic. We did our final rehearsal outside of the library.
+
+On performance day, I brought my Tigerente. Realizing I don't have
+a string to pull him with, I used my USB microphone with a clip on one
+end.
+
+The performance went smoothly. The other three groups also did
+a spectacular job. Their roleplays were:
+
+- Rotkäppchen-artig (the little red riding hood with a twist)
+- Geistjäger (ghostbusters)
+- Fortnite (featuring lines such as „Ach! Ich bin gestorben!“ and a sheet
+ of paper that just said "GUN")
+
+Personally I enjoyed Rotkäppchen-artig the most but somehow Geistjäger was
+nominated. Oh yeah our play was nominated too.
+
+The competition was set two days later. That day I learned that the
+Geistjäger cast couldn't make it, so Rotkäppchen would be competing with
+us after all. The other two competitors were from 102, bringing »der
+Froschprinz« (the frog prince, which featured a talking piece of bread)
+and »der Urlaub« (the vacation, set in Switzerland, that somehow ended in
+a guy suffering diarrhea).
+
+Right after our performance, I handed my Tigerente to Hartmut (_the_
+German department professor, who starred all the grammar videos) for his
+autograph. He agreed.
+
+After all four groups have performed, we and the professors were asked to vote.
+We won, and Rotkäppchen came second. Tied third are the other two groups.
+
+Absolutely wonderful.
+</details>
+
+#### Favorite moments
+
+- Rollenspiel of course
+- When Laura gave us a Kahoot quiz on a Zimtsterne recipe and I was the
+ only one who got the "convert 150 ℃ to ℉" question right (thanks to
+ KRunner) and I got a copy of her recipe
+
+### MUSIC 210
+
+Rating: __4/5__
+
+Theme: Rap songwriting
+
+#### Instructor (Deidre Smith, aka D. S. SENSE)
+
+She's a nice lady who unfortunately missed a lot of lectures due to illness.
+Nevertheless, she was nothing but encouraging to every one of us.
+
+Creativity does not constrain me; language is the barrier. I can sometimes fail
+to understand her accent, and I'm not a fluent English speaker when it comes to
+rap. I took this course partly to train my spoken English, but it didn't seem
+to work that well. (skill issue)
+
+Every in-person class we would be given 25 minutes to work on a short piece of
+hip hop. I abused this opportunity to:
+
+- rant about NFTs
+- write a piece that goes between 7/8, 6/8, and 9/8 time
+- ask people in the room to decide on my grocery list
+
+Anyway, this course gave birth to my first original song, [This Song Will
+Uncure Your Depression](../music/uncure-your-depression.md).
+
+### CHEM 130
+
+Rating: __4.5/5__ (relative to my expectations and wishes, which are low)
+
+I took CHEM 210 two years ago and sucked so much it wasn't even transferable.
+I had to repeat it to fill in that hole on my transcript. But here, I almost
+know everything, and routinely get full points on the tests.
+
+There are two hypotheses why I sucked:
+
+- CHEM 210 is inherently harder than 130
+- skill issue
+
+I believe both factors are at play. On one hand 210 defintely covered something
+extra (entropy and electrochemistry), but on the other at that time I was
+a fresh college student who didn't even know how college worked. This course
+was chemistry "principles". It's nothing like high school chemistry that's just
+"remember this experiment" and "remember that phenomenon". Instead it's built
+upon physical and statistical principles, which I did _not_ know at that time.
+Now that I've taken a course on electromagnetics and one on statistics, I can
+actually understand what's going on with these "dipoles" (they're just two
+opposite charges on a stick) and "electron clouds" (they're just probability
+density functions). This is why I wonder why this course is so often taken in
+the very first semester of college, when it's not a prerequisite for any other
+ECE course. In fact, that makes the experience a little like high school
+— learning the "principles" without learning the principles of the principles.
+
+## General comments & concerns
+
+- I am aware that, as far as infrastructure goes, universities are among the
+ best places in the US. But even so, there's not enough buses.
+- Food too expensive. But not terrible.
+- Drinks too expensive.
+- The nature is well preserved and air quality is good.
+- It snows sometimes which is fun if I wasn't waiting for a bus outside.
+- I have to learn to slow down. When I'm walking in a dining hall, my instincts
+ urge me to hurry up, which is not a good strategy in a place packed with
+ people carrying plates.
+- People here are more welcoming and diverse than expected. I have witnessed
+ zero cases of xenophobia or racism so far.
+- I wanted to get an FCC ham radio license but never got around to. Next
+ semester I guess?
+
+## Significant material gains
+
+- I bought a blåhaj
+- I bought a bass
+- I bought a laptop
+- I got a "Michigan Pride" T-shirt at the drag show
+- I got a "Know two ways out" shirt at a fire safety awareness event
+- I got an "Arts & Resistance" T-shirt at the Duderstadt gallery
+- I bought a second-hand CD of _The Black Parade_
+- I bought two books "just because" and three more for reasons
+
+## Works Cited
+
+- Janosch. _Oh, wie schön ist Panama_. Beltz & Gelberg, 1978.
+- Myllynen, Milka. "Was schätzen Leser an einem Kinderbuchklassiker?
+ Aufgezeigt anhand von Kundenrezensionen auf Amazon.de zum Kinderbuch
+ „Oh, wie schön ist Panama“." Tampere University, April 2019. [PDF
+ version on
+ tuni.fi](https://trepo.tuni.fi/bitstream/handle/10024/105532/MyllynenMilka2019.pdf).
+ Accessed 2023-11-07.
diff --git a/docs/umich/img/f23_winter_break/broccoli_goo.jpg b/docs/umich/img/f23_winter_break/broccoli_goo.jpg
new file mode 100644
index 0000000..7f416c4
--- /dev/null
+++ b/docs/umich/img/f23_winter_break/broccoli_goo.jpg
Binary files differ
diff --git a/docs/umich/img/f23_winter_break/broccoli_soup.jpg b/docs/umich/img/f23_winter_break/broccoli_soup.jpg
new file mode 100644
index 0000000..1138210
--- /dev/null
+++ b/docs/umich/img/f23_winter_break/broccoli_soup.jpg
Binary files differ
diff --git a/docs/umich/img/f23_winter_break/broken_pipe.jpg b/docs/umich/img/f23_winter_break/broken_pipe.jpg
new file mode 100644
index 0000000..02d6499
--- /dev/null
+++ b/docs/umich/img/f23_winter_break/broken_pipe.jpg
Binary files differ
diff --git a/docs/umich/img/f23_winter_break/bunny.jpg b/docs/umich/img/f23_winter_break/bunny.jpg
new file mode 100644
index 0000000..924d303
--- /dev/null
+++ b/docs/umich/img/f23_winter_break/bunny.jpg
Binary files differ
diff --git a/docs/umich/img/f23_winter_break/mushroom_soup.jpg b/docs/umich/img/f23_winter_break/mushroom_soup.jpg
new file mode 100644
index 0000000..4cc48ac
--- /dev/null
+++ b/docs/umich/img/f23_winter_break/mushroom_soup.jpg
Binary files differ
diff --git a/docs/umich/img/f23_wrapup/badapple.png b/docs/umich/img/f23_wrapup/badapple.png
new file mode 100644
index 0000000..3e33ad3
--- /dev/null
+++ b/docs/umich/img/f23_wrapup/badapple.png
Binary files differ
diff --git a/docs/umich/img/f23_wrapup/tigerente.jpg b/docs/umich/img/f23_wrapup/tigerente.jpg
new file mode 100644
index 0000000..f61ecc2
--- /dev/null
+++ b/docs/umich/img/f23_wrapup/tigerente.jpg
Binary files differ
diff --git a/docs/umich/index.md b/docs/umich/index.md
new file mode 100644
index 0000000..ecb443b
--- /dev/null
+++ b/docs/umich/index.md
@@ -0,0 +1,7 @@
+# University of Michigan
+
+so basically i live in ann arbor now
+
+- [Lessons in the US of A](us_lessons.md)
+- [Fall 2023 Wrapup](f23_wrapup.md)
+- [Fall 2023 winter break wrapup](f23_winter_break.md)
diff --git a/docs/umich/us_lessons.md b/docs/umich/us_lessons.md
new file mode 100644
index 0000000..7fcd5e6
--- /dev/null
+++ b/docs/umich/us_lessons.md
@@ -0,0 +1,115 @@
+# Lessons in the US of A
+
+Recently I made the mistake of living in the US of A, the paramount
+capitalist hell of the modern world. As a consequence, I had to relearn
+some facts, because everything works different here.
+
+## August 2023: Moving in
+
+### The good
+
+- They put free pads and tampons even in the men's bathroom
+- I can ride the bus for free with my student card
+- By custom I should greet the bus driver, who will greet back
+- Food comes in absolutely gigantic portions
+- Central campus is much more walkable than I thought
+- A French press is very easy to use and much cheaper than a coffee
+ machine
+- To request a stop on a bus you just pull the rope-y thing
+- You can take all the time you need to get off the bus. No pressure to
+ stand up in advance.
+- Pedestrians have absolute right of way, I hypothesize you could even
+ cross the street blindfolded and not get hit by a car
+
+### The bad
+
+- Power outages happen every year and somehow people are okay with it and
+ do nothing to improve the infrastructure
+- Toilet paper comes in single ply by default
+- Apartments don't have ceiling lights and rely on floor lamps. There is
+ a dedicated outlet wired to a light switch which I found by poking with
+ a multimeter
+- Everything in the supermarket seems reasonably priced as long as you
+ don't convert it to your home currency
+- The washing machine isn't working??
+- Power failure?? In IKEA???
+- The carpet makes my desk jiggly
+- Drinks are most often cold, even chilled. Even chocolate.
+- Internet failure?? In a university???
+
+### The neither
+
+- Kroger closes at 10 pm
+- The cord to a power strip is very, _very_ thicc
+- My apartment has a coaxial port for internet which I've never seen in my
+ life
+- Michigan law does not require you to have a front license plate
+
+## September 2023: Settling down
+
+### The good
+
+- They hand out free shirts and swag at the pride event
+- Drag shows, fully unhinged
+- I can just _get_ a Kroger card? Like for free?
+- I found Three Cheers posters on sale
+- tfw fresh clothes from a dryer. hmmm
+- Internet is super fast
+- There are no stray animals here (with the side effect that I have not
+ seen a single cat on this land)
+- One month in and I've seen three people in band merch T-Shirt of quality
+ taste
+- None of the instructors read from the slides; they really go to great
+ lengths explaining stuff
+
+### The bad
+
+- Ann Arbor buses just… stop operating at 20:00 on Sunday?? (Update: this
+ might have been related to labor day)
+- _One_ bus per hour?? Unimaginable in Shanghai
+- I got charged $3 just for inserting my bank card into another bank's ATM
+- $15 tax on a bass. $50 tax on a laptop.
+- Campus ethernet broke and so did teaching infrastructure, e.g.
+ autograders
+- Huge thanks to the Amazon driver who attempted to deliver my package on
+ a Sunday morning at 6:50 AM. Why would Amazon send a worker to deliver
+ my package this early
+- Oh so Xfinity broke too? What the fuck is wrong with American
+ infrastructure
+
+### The neither
+
+- My textbook is "loose leaf", which means a pile of pages I have bind
+ myself. Pro: I can carry only the chapters I want. Con: I have to use
+ zipties as a dirty hack before my binder arrives
+
+## October 2023: Speeding up
+
+### The good
+
+- There are record stores within walking distance from central campus
+- I found some rare vinyls there (but didn't buy them)
+- You don't have a seatplan in exams so you can sit anywhere in your
+ assigned room
+
+### The bad
+
+- OK I got my Amazon package (new laptop) one week later but it's the
+ wrong color
+- Fucking Lenovo. ThinkPads had _one_ killer feature and you killed it.
+ Bring back the 1.8mm travel keyboard you cowards
+- Should have bought Gen 2 instead of Gen 3. Also should have made sure
+ about the color.
+- Downtown Ann Arbor is nothing like the university campus. There are
+ beggars, jaywalkers, disabled people who struggle to wheelchair
+ themselves across the sloped street, and that one guy who yells for some
+ reason. Says a lot about society
+
+### The neither
+
+- None
+
+## End
+
+As of 2024-01-11 I feel there's not much to add, because my transition to
+life in the US of A seems complete. bruh
diff --git a/mkdocs.yml b/mkdocs.yml
index 2c1dceb..8f386ef 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -3,3 +3,14 @@ site_url: https://fkfd.me/
theme:
name: null
custom_dir: alabaster-lite/
+extra:
+ sidebars:
+ - title
+ - toc
+ homepage_sidebars:
+ - title
+ - links
+markdown_extensions:
+ - md_in_html
+ - toc:
+ permalink: true