The basics nobody bothers to explain properly
Raycasting is a rendering technique that shoots out rays from a camera point and checks where they hit geometry. That's it. The rest is just optimizations and compromises. Most people come to this from retro game dev because Wolfenstein 3D made it famous, but the math has been around since the 60s and it's used in everything from pathtracing in modern engines to simple collision detection systems. Here's how it actually works when you're sitting down to implement one. You pick a pixel on your screen. From the camera position, you calculate a direction vector for that pixel using the field of view and the pixel's screen coordinates. Then you step along that ray, checking against your scene's collision surfaces at each interval. The first surface the ray hits becomes the color of that pixel. Repeat for every pixel. Simple description, mildly tedious implementation.
Implementing Raycasting from scratch
I'll walk through the straightforward DDA algorithm approach since it's the most common one people need. You define your camera plane, your ray direction per pixel, and then you march through the grid or geometry using the Digital Differential Analyzer method. It avoids expensive square roots and division inside your main loop, which matters when you're doing this per frame at 60fps across potentially thousands of pixels. Start by setting up your camera. You need an origin point, usually at 0,0,0 or wherever your player sits, and a view plane distance that determines your field of view. A view plane distance of 1.0 with a screen width of 640 gives you roughly a 60-degree horizontal FOV, which is comfortable for most games. If you make it too narrow the world looks compressed. Too wide and your edges start distorting noticeably unless you're doing fisheye correction. The actual ray marching is where things get interesting. For each column of pixels, you compute your ray direction. Then you calculate the side distance deltas — how far the ray travels horizontally versus vertically to cross a grid line. The smaller of the two tells you which axis to advance next. This keeps your steps efficient. A poorly implemented version that checks every single unit along the ray instead of jumping grid-to-grid will be orders of magnitude slower, sometimes 200 to 400 times slower depending on your scene complexity.
I ran into a specific issue once where the ray was always missing thin walls by a fraction of a unit, causing visible gaps that looked like the walls were flickering when the player moved parallel to them. The fix was straightforward but not obvious if you haven't dealt with it before — I was using floating point comparison with equality checks on the grid crossing points. Changing to a small epsilon threshold on the side distance comparison eliminated the ghost gaps entirely. Also switched to using a slightly larger step multiplier, something like 1.0001 instead of exactly 1.0, to push the ray past the hit surface on the next iteration and avoid counting the same wall twice.
Get the Full Details

Common pitfalls that will waste your weekend
One thing beginners consistently miss is that raycasting is fundamentally a one-bounce system. Every ray you shoot hits exactly one surface and stops. If you want reflections or shadows, you need to shoot additional rays from that hit point, and each one multiplies your workload. A single reflection pass on a 640 by 480 screen means roughly 307,200 ray queries minimum, and that's without accounting for any secondary bounces. On a CPU this runs at maybe 5 to 15 frames per second depending on your scene. On a GPU it's trivial, but if you're doing this in software like most retro-style projects, you need to be strategic about which pixels actually get cast rays. Another counter-intuitive detail: perpendicular walls render differently than diagonal ones even with identical DDA implementations. A wall running exactly north-south will be crossed column by column with consistent vertical spacing, but a 45-degree wall will have artifacts where the ray stepping skips entire segments. The fix is either to increase your step resolution or to use a different approach for diagonal surfaces. I ended up switching to a hybrid method where orthogonal walls used standard DDA and angled walls used abresampler approach, which cut the visual artifacts without a measurable performance hit. There are also edge cases with corners and T-junctions where two adjacent surfaces share an edge but have slightly different normals. Your ray might hit the same geometric point from both surfaces and produce z-fighting or texture seam visible seams, especially if you're doing any kind of perspective-correct texture mapping. The workaround is to offset the hit point slightly along the surface normal by a small value, usually around 0.001 in world units, to push the texture lookup away from the exact intersection.
Let's talk about what this technique cannot do well. Raycasting does not handle curved surfaces natively. You can approximate them with many flat polygons, but that increases your ray-triangle intersection count linearly and defeats some of the efficiency gains. Spheres and cylinders require separate intersection functions that are more computationally expensive than axis-aligned box checks. If your scene is mostly hard surfaces like rooms and corridors, raycasting is fast. If you're trying to render terrain or organic shapes, you're fighting the method the whole time. The other hard limitation is depth. A basic raycaster gives you distance to the nearest surface, but nothing about what's behind it. Occlusion culling requires you to sort surfaces by distance and only render what's visible, which adds another pass. Transparent surfaces are essentially impossible to handle correctly without sorting every fragment, which turns your O(n) raycast into an O(n log n) operation. Most people just skip transparency entirely in their raycaster and move on. If you need soft shadows, global illumination, or complex material responses, raycasting alone won't get you there. You'd be better off looking at photon mapping or even just using a standard rasterization pipeline with shadow maps, which modern GPUs handle in hardware at resolutions raycasting simply can't match. The technique is still useful for specific applications like indoor corridor rendering, voxel-based worlds, and retro-style games where the aesthetic is intentional, but it's not a general-purpose rendering solution.
The code itself is relatively short. A functional 2D raycaster with DDA stepping, texture mapping, and basic wall shading runs in about 300 to 500 lines depending on whether you're rolling your own math library or using something like GLM. I've seen complete implementations in under 200 lines when the author cuts corners on texture filtering and skips anti-aliasing, but those versions look rough at higher resolutions. The sweet spot for readability and performance is somewhere around 400 lines with bilinear filtering and a fixed-point math fallback for platforms without floating point support. Download and reference implementations exist across a few places. The classic example is the tutorial series on cprogramming.com that walks through a C-based raycaster step by step. For more modern approaches, the GitHub repository raycaster by various contributors has a clean WebGL version that runs in the browser and shows real-time performance numbers. There's also the infamous "The New Method" article from 2005 that restructured how DDA is taught, which helped a lot of people actually understand what the algorithm was doing instead of just copying code. If you're working in a game engine, both Unity and Unreal have raycast query functions built in, though they're more collision detection tools than rendering pipelines. The best way to learn this is to write a dumb version first. Cast rays only horizontally, render vertical strips with no texture mapping, just flat colors based on distance. Get that working and you'll understand the core loop in an afternoon. Then add vertical casting. Then add textures. Then add lighting. Each step reveals where the previous assumptions were wrong. I spent three days debugging a version that rendered perfectly until I rotated the camera, at which point every wall disappeared. Turned out my ray direction calculation assumed the camera was always aligned to the grid axes. Adding a rotation matrix to the ray direction computation fixed it immediately.

When raycasting is the right call and when it isn't
Use it when your scene is mostly axial or grid-based, when you need deterministic performance without a GPU, or when the aesthetic of scanline rendering is part of the design. It's also genuinely useful for collision detection in 2.5D environments where full 3D physics would be overkill. Avoid it when you need photorealism, complex lighting, transparent materials, or scenes with significant curvature. In those cases, standard rasterization or a proper path tracer will give you better results in less development time.