Discuss Scratch
- Discussion Forums
- » Advanced Topics
- » [FULL TUTORIAL!] How Ray Tracing is accomplished in Scratch!
- voxels1234
-
Scratcher
43 posts
[FULL TUTORIAL!] How Ray Tracing is accomplished in Scratch!
As of when I created this topic (august 9, 2026), I am a new scratcher
So sadly I can't add images in this topic. I tried my best
for it to be as beginner-friendly as possible.
Hello Scratchers! This topic is to discuss how ray tracing is professionally accomplished in Scratch!
Before I start with the tutorial, let's start with the basics. What even is raytracing?
Ray tracing is a 3D rendering technique where rays are traced through a scene to determine what geometry they intersect; it's widely used for realistic lighting. Many popular games support ray tracing, with the most popular example being Cyberpunk 2077. Ray tracing is done by going pixel to pixel, checking if a pixel should be drawn on that pixel and of what colour by casting a ray on that pixel and checking if it intersects with a shape. The easiest shapes to raytrace are cubes, spheres and infinite planes.
You can find an example of one of my raytraced projects here:
https://scratch.mit.edu/projects/1375359681/
Now let's start with the tutorial!
A really important clarification is that raytracing is very expensive even on high-end hardware; that's why
We will scale the pixels just enough to render easily.
Scratch normally has a 480x360 pixel viewport, which is precisely 172800 pixels. That's too much for Scratch to smoothly handle.
Let's scale it down a bit. What if each pixel gets scaled by 4 times?
Let's calculate it: 172800/(4*4) = 172800/16 = 10800
That is 16x fewer pixels! Amazing!
Let's start with the core loop:
This loops through all pixels on the screen and casts a ray
But we need a real object system, lets edit our green flag block to actually generate a sphere!
Now let's work on the raytracer itself! Before I start with the script
For the sphere raytracer itself, let's see the formula for sphere-ray intersection.
You can skip this if you want.
Now time to implement it in Scratch!
You can stop here if you want, but if you want to make it even more realistic, let's add one final lighting technique for this tutorial.
It's called ‘directional light’ (done using Lambertian).
Update your ‘when green flag clicked’ block:
Now find this section of the code in your raytrace pixel block:
Replace it with this new version:
Beware that this lambert light doesn't include shadow rays, which is a laggier and more complex addon.
Please report any issues you have with this exact code, and I will be more than happy to debug them!
So sadly I can't add images in this topic. I tried my best
for it to be as beginner-friendly as possible.
Hello Scratchers! This topic is to discuss how ray tracing is professionally accomplished in Scratch!
Before I start with the tutorial, let's start with the basics. What even is raytracing?
Ray tracing is a 3D rendering technique where rays are traced through a scene to determine what geometry they intersect; it's widely used for realistic lighting. Many popular games support ray tracing, with the most popular example being Cyberpunk 2077. Ray tracing is done by going pixel to pixel, checking if a pixel should be drawn on that pixel and of what colour by casting a ray on that pixel and checking if it intersects with a shape. The easiest shapes to raytrace are cubes, spheres and infinite planes.
You can find an example of one of my raytraced projects here:
https://scratch.mit.edu/projects/1375359681/
Now let's start with the tutorial!
A really important clarification is that raytracing is very expensive even on high-end hardware; that's why
We will scale the pixels just enough to render easily.
Scratch normally has a 480x360 pixel viewport, which is precisely 172800 pixels. That's too much for Scratch to smoothly handle.
Let's scale it down a bit. What if each pixel gets scaled by 4 times?
Let's calculate it: 172800/(4*4) = 172800/16 = 10800
That is 16x fewer pixels! Amazing!
Let's start with the core loop:
when green flag clicked
set [screen res v] to [4] //This scales pixels by 4x which is a ~16x performance boost
set [focal length v] to [240] // You can edit this; lower focal means a higher FOV and vice-versa, 240 is recommended for Scratch.
forever
raytrace
end
define raytrace
erase all
set [width v] to ([ceiling v] of ((480)/(screen res))::operators)
set [height v] to ([ceiling v] of ((360)/(screen res))::operators)
set [y v] to (-180)
repeat (height)
set [x v] to (-240)
repeat (width)
raytrace pixel (x)(y)
change [x v] by (screen res)
end
change [y v] by (screen res)
end
define raytrace pixel (x) (y)
This loops through all pixels on the screen and casts a ray
But we need a real object system, lets edit our green flag block to actually generate a sphere!
when flag clicked
set [focal v] to (240)
set [screen res v] to (4)
delete all of [x v]
delete all of [y v]
delete all of [z v]
delete all of [radius v]
delete all of [r v]
delete all of [g v]
delete all of [b v]
create sphere at (0) (0) (30) with radius: (15) and base color: (255) (0) (0) // this creates a red sphere
forever
raytrace
end
define create sphere at (sphere_x) (sphere_y) (sphere_z) with radius: (sphere_radius) and base color: (sphere_r) (sphere_g) (sphere_b)
add (sphere_x) to [x v]
add (sphere_y) to [y v]
add (sphere_z) to [z v]
add (sphere_radius) to [radius v]
add (sphere_r) to [r v]
add (sphere_g) to [g v]
add (sphere_b) to [b v]
Now let's work on the raytracer itself! Before I start with the script
For the sphere raytracer itself, let's see the formula for sphere-ray intersection.
You can skip this if you want.
We start by calculating ray direction like this:
Do the following for every pixel:
ray x = x / focal
ray y = y / focal
ray z = 1 // set this to 1 since we aren't going to use camera rotation in this tutorial
Now we normalise this ray:
dx = normalize_ray(ray x,ray y,ray z).x
dy = normalize_ray(ray x,ray y,ray z).y
dz = normalize_ray(ray x,ray y,ray z).z
The formula for normalising is
len = sqrt(x*x + y*y + z*z)
normalized x = x/len
normalized y = y/len
normalized z = z/len
Now we need to have a closest point for correct sorting:
closest = 1/0 (if you have a render distance, replace it with that)
The following has to be done for every sphere in the map (here, 1 because we only have 1 sphere)
We calculate lx, ly and lz
lx = sphere x - cam x
ly = sphere y - cam y
lz = sphere z - cam z
Note: If your camera is fixed at 0, 0, 0, then you can just set lx to sphere x, ly to sphere y and the same for lz.
Now we calculate dot (L, L).
LL = lx*lx + ly*ly + lz*lz
And vector LD
LD = lx*dx + ly*dy + lz*dz
note: dx, dy, and dz is the normalized ray direction we calculated earlier
if vector ld < 0, then you can skip this sphere
Now calculate d squared:
dsq = LL - LD*LD
Again, skip this sphere if dsq < sphere rad*sphere rad (usually <= instead of < but the difference is usually too tiny visually)
Now calculate the 2 possible hit points:
thc = sqrt(sphere rad*sphere rad - dsq)
t1 = LD - thc
t2 = LD + thc
hit dist = 0
Now if t1 > 0, then set hit distance to t1.
Else (if t1 is not greater than 0), if t2 > 0, then set hit distance to t2.
Now skip sphere if the hit distance is not > 0
Also skip sphere if hit dist > closest
If the sphere reaches this far, we can draw a pixel with the sphere color at this pixel!
Now time to implement it in Scratch!
define raytrace pixel (x)(y)
set [ray x v] to ((x)/(focal))
set [ray y v] to ((y)/(focal))
set [ray z v] to (1)
set [len v] to ([sqrt v] of ((((ray x)*(ray x))+((ray y)*(ray y)))+((ray z)*(ray z)))::operators)
set [dx v] to ((ray x)/(len))
set [dy v] to ((ray y)/(len))
set [dz v] to ((ray z)/(len))
set [closest v] to ((1)/(0)) // change this to render distance if you have one
set [i v] to (1)
repeat (length of [x v])
set [sphere x v] to (item (i) of [x v])
set [sphere y v] to (item (i) of [y v])
set [sphere z v] to (item (i) of [z v])
set [sphere radius v] to (item (i) of [radius v])
set [lx v] to ((sphere x)-(camera x))
set [ly v] to ((sphere y)-(camera y))
set [lz v] to ((sphere z)-(camera z))
set [LL v] to (((lx)*(lx))+(((ly)*(ly))+((lz)*(lz))))
set [LD v] to (((lx)*(dx))+(((ly)*(dy))+((lz)*(dz))))
if <(LD) > (0)> then
set [dsq v] to ((LL)-((LD)*(LD)))
if <(dsq)<((sphere radius)*(sphere radius))> then
set [thc v] to ([sqrt v] of (((sphere radius)*(sphere radius))-(dsq))::operators)
set [t1 v] to ((LD)-(thc))
set [t2 v] to ((LD)+(thc))
set [hit dist v] to (0)
if <(t1)>(0)> then
set [hit dist v] to (t1)
else
if <(t2)>(0)> then
set [hit dist v] to (t2)
end
end
if <<(hit dist) > (0)> and <(hit dist) < (closest)>> then
set [closest v] to (hit dist)
go to x:(x) y:(y)
set [pixel r v] to ([floor v] of (item (i) of [r v]))
set [pixel g v] to ([floor v] of (item (i) of [g v]))
set [pixel b v] to ([floor v] of (item (i) of [b v])) // optionally clamp rgb from 0-255 though not required if you don't have lighting effects
set pen color to (((pixel r)*(65536))+(((pixel g)*(256))+(pixel b))) // make sure you use the correct pen color block which has the color picker
set pen size to ([ceiling v] of ((screen res)*(1.5))::operators)
pen down
pen up
end
end
change [i v] by (1)
end
You can stop here if you want, but if you want to make it even more realistic, let's add one final lighting technique for this tutorial.
It's called ‘directional light’ (done using Lambertian).
Update your ‘when green flag clicked’ block:
when green flag clicked
previous code here
set light direction to (0)(1)(0) // light comes straight from the top. (also don't set it to 0,0,0)
forever
raytrace
end
define set light direction to (x)(y)(z)
set [light length v] to ([sqrt v] of ((x)*(x))+(((y)*(y))+((z)*(z)))::operators)
set [light dir x v] to ((x)/(light length))
set [light dir y v] to ((y)/(light length))
set [light dir z v] to ((z)/(light length))
Now find this section of the code in your raytrace pixel block:
set [closest v] to (hit dist)
go to x:(x) y:(y)
set [pixel r v] to ([floor v] of (item (i) of [r v]))
set [pixel g v] to ([floor v] of (item (i) of [g v]))
set [pixel b v] to ([floor v] of (item (i) of [b v])) // optionally clamp rgb from 0-255 though not required if you don't have lighting effects
set pen color to (((pixel r)*(65536))+(((pixel g)*(256))+(pixel b))) // make sure you use the correct pen color block which has the color picker
set pen size to ([ceiling v] of ((screen res)*(1.5))::operators)
pen down
pen up
Replace it with this new version:
set [closest v] to (hit dist)
go to x:(x) y:(y)
set [pixel r v] to ([floor v] of (item (i) of [r v]))
set [pixel g v] to ([floor v] of (item (i) of [g v]))
set [pixel b v] to ([floor v] of (item (i) of [b v])) // since we use a lighting technique, we should add clamping now
set [hit x v] to ((camera x) + ((hit dist)*(dx)))
set [hit y v] to ((camera y) + ((hit dist)*(dy)))
set [hit z v] to ((camera z) + ((hit dist)*(dz)))
set [nx v] to ((hit x)-(sphere x))
set [ny v] to ((hit y)-(sphere y))
set [nz v] to ((hit z)-(sphere z))
set [normal length v] to ([sqrt v] of (((nx)*(nx))+(((ny)*(ny))+((nz)*(nz))))::operators)
set [nx v] to ((nx)/(normal length))
set [ny v] to ((ny)/(normal length))
set [nz v] to ((nz)/(normal length))
set [lambert v] to (((nx)*(light dir x))+(((ny)*(light dir y))+((nz)*(light dir z))))
if <(lambert)<(0)> then
set [lambert v] to (0)
end
set [final brightness v] to ((0.3)+((lambert)*(0.7))) // add ambient of 0.3 so that the side facing away from sun doesn't go black
set [pixel r v] to ((pixel r)*(final brightness))
set [pixel g v] to ((pixel g)*(final brightness))
set [pixel b v] to ((pixel b)*(final brightness))
if <(pixel r)>(255)> then
set [pixel r v] to (255)
end
if <(pixel g)>(255)> then
set [pixel g v] to (255)
end
if <(pixel b)>(255)> then
set [pixel b v] to (255)
end
if <(pixel r)<(0)> then
set [pixel r v] to (0)
end
if <(pixel g)<(0)> then
set [pixel g v] to (0)
end
if <(pixel b)<(0)> then
set [pixel b v] to (0)
end
set pen color to (((pixel r)*(65536))+(((pixel g)*(256))+(pixel b))) // make sure you use the correct pen color block which has the color picker
set pen size to ([ceiling v] of ((screen res)*(1.5))::operators)
pen down
pen up
Beware that this lambert light doesn't include shadow rays, which is a laggier and more complex addon.
Please report any issues you have with this exact code, and I will be more than happy to debug them!
Last edited by voxels1234 (Sept. 1, 2026 14:40:02)
- Blockbuddies123
-
Scratcher
18 posts
[FULL TUTORIAL!] How Ray Tracing is accomplished in Scratch!
Bro. This is AMAZING! Love the tutorial.
- voxels1234
-
Scratcher
43 posts
[FULL TUTORIAL!] How Ray Tracing is accomplished in Scratch!
Bro. This is AMAZING! Love the tutorial.np bro!
- Johnliu201624
-
Scratcher
5 posts
[FULL TUTORIAL!] How Ray Tracing is accomplished in Scratch!
i think i have a problem in my code. The shadows rendered more green and randomly colored which is not what i want. Pls help me
- Johnliu201624
-
Scratcher
5 posts
[FULL TUTORIAL!] How Ray Tracing is accomplished in Scratch!
Screenshot_20-9-2026_211745_turbowarp.org.jpeg
copy link
copy link
- voxels1234
-
Scratcher
43 posts
[FULL TUTORIAL!] How Ray Tracing is accomplished in Scratch!
Screenshot_20-9-2026_211745_turbowarp.org.jpegI can't use that since the screenshot is saved on your device, you can use a website like cubeupload to show images or share the project. It shouldn't have shadows in this tutorial but the random colors is probably an issue, it will be best if you can share your project for a while so that I can review the project for any bugs
- Johnliu201624
-
Scratcher
5 posts
[FULL TUTORIAL!] How Ray Tracing is accomplished in Scratch!
The project is called 3d raytracer. This is the actual link.Hope you can copy.
https://scratch.mit.edu/projects/1383238628/
https://scratch.mit.edu/projects/1383238628/
- Johnliu201624
-
Scratcher
5 posts
[FULL TUTORIAL!] How Ray Tracing is accomplished in Scratch!
also use turbowarp
- Johnliu201624
-
Scratcher
5 posts
[FULL TUTORIAL!] How Ray Tracing is accomplished in Scratch!
actually I fixed it by rounding, sorry for the inconvenience.
- voxels1234
-
Scratcher
43 posts
[FULL TUTORIAL!] How Ray Tracing is accomplished in Scratch!
actually I fixed it by rounding, sorry for the inconvenience.no problem

- Discussion Forums
- » Advanced Topics
-
» [FULL TUTORIAL!] How Ray Tracing is accomplished in Scratch!