Raytracer - Raylib

Let’s walk through src/main.c – a clean raytracer using raylib.

Check the project’s source code at Github

The materials

  • SphereMaterial: Holds Color, specular (0-1), shininess.
  • Sphere: Material + Vector3 position + float radius.

Render Loop

Per frame, recompute all pixels (brute-force, no acceleration):

  1. Ray Generation: For each x,y, GetScreenToWorldRay shoots ray from camera through pixels.

  2. Intersection:

    • Init nearest collision distance to FLT_MAX.
    • For each sphere: GetRayCollisionSphere(ray, pos, radius).
    • Track closest hit + sphere.
  3. Miss: BLACK pixel.

[Read more]

Raytracer - SDL

Building a 2D Raytracer in 150 Lines of C

A real-time 2D simulation where a point light source emits rays in all directions. When these rays hit an obstacle, they stop. The area behind the obstacle receives no light.

Here’s what the simulation does:

  • A white circle acts as a light source, emitting yellow rays
  • Another white circle acts as an obstacle that blocks light
  • The obstacle bounces up and down automatically
  • You can drag the light source around with your mouse to see how shadows change in real-time

The Foundation

Every raytracer needs to represent two fundamental concepts: shapes and rays.

[Read more]

Introducing Dirt - Keep your git repositories clean

Dirt is a lightweight command-line tool built in Go that helps developers maintain clean Git repositories across their workspaces. It scans specified directories (and subdirectories up to 2 levels deep) for Git repos, checking each for uncommitted changes or unpushed commits.

Check out the project source code on Github

The Problem It Solves

In large codebases or multi-project setups, it’s easy to forget about local changes or commits that haven’t been pushed. Dirt automates the discovery of “dirty” repositories, saving time and preventing lost work or merge conflicts.

[Read more]

Building a JSX Transpiler with Vite

Vite Setup

Bootstrap a new vite project using vanilla template.

npm create vite@latest jsx-renderer-vite -- --template vanilla

Enter the project and install the dependencies

cd jsx-renderer-vite && npm install

Then, adjust the project files so that the final structure is like this

.
├── src/
│   └── main.jsx
├── .gitignore
├── index.html
├── package-lock.json
├── package.json
└── vite.config.js

The following files are important for this setup process

// vite.config.js

import * as v from "vite";

export default v.defineConfig({
  esbuild: {
    // use h as the JSX Factory function instead of React.createElement
    jsxFactory: "h",
    // use Fragment for JSX fragments (<>)
    jsxFragment: "Fragment",
  },
});
// package.json

{
  "name": "jsx-renderer-vite",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "devDependencies": {
    "vite": "^7.1.7"
  }
}
<!-- index.html -->

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>jsx-renderer-vite</title>
  </head>
  <body>
    <div id="app"></div>
    <!-- change the value of "src" here to main.jsx -->
    <script type="module" src="src/main.jsx"></script>
  </body>
</html>

Creating the h Function

Now let’s implement the h() function that esbuild will call for each JSX element.

[Read more]

Neovim Plugin Testing with Mocks: Customizing Child Neovim Instances in lazydocker.nvim

Testing Neovim plugins is tricky: side effects like spawning jobs (lazydocker), creating windows, notifications, etc.

In lazydocker.nvim tests, we use a mock system to:

  • Simulate missing executables (docker, podman, lazydocker)
  • Capture jobstart params (cmd, env)
  • Stub window APIs (nvim_win_is_valid, nvim_open_win)
  • Intercept vim.notify for error verification

Powered by mini.test child Neovim instances.

Child Neovim: Isolated Sandboxes

Bootstrap:

local child = helpers.new_child_neovim()  -- MiniTest child
child.setup()  -- Minimal init: readonly=false, small viewport (15x40)
child.load_lzd(config)  -- require('lazydocker').setup(config)
child.unload_lzd()  -- Cleanup: package.loaded, globals, augroups

Lifecycle management:

[Read more]