How to make my voxel rasteriser run faster?
07:58 15 Jul 2026

I'm making a terrain generator with a voxel rasteriser that I coded but it is ungodly slow even with just 25 blocks. I think it may be a turtle issue but if theres any other ways i could optimise then please help. thank you.

The way the code works is in the bottom Blockquote, it calculates the angle the mouse has to the centre of the screen before moving the mouse back there, and sends that angle together with voxel coordinates to the first Blockquote.

the first Blockquote is just a simple graphics pipeline (without z-buffering) and the "block()" function creates all the tris neccesary to construct the block, and Turtle does all the drawing.

#===============================================================================
#= Logic Layer                                                                =#
#===============================================================================

#libraries----------------------------------------------------------------------

from  math import sin, cos, acos
from numpy import matmul, array, float64
from turtle import Turtle, Screen
from time import sleep

s =  Screen()
s.delay(0)
s.tracer(0,0)
s.screensize(200,200)
t = Turtle()
t.speed(0)
t.penup()
t.shape("square")
t.shapesize(0.5,0.5)
t.ht()

#translation--------------------------------------------------------------------

def translateX(pos,distance):
    pos[0] += distance
    return pos

def translateY(pos,distance):
    pos[1] += distance
    return pos

def translateZ(pos,distance):
    pos[2] += distance
    return pos

#rotation-----------------------------------------------------------------------

def rotateX(pos, cosT, sinT):

    A = array(pos[1:])

    B = array([[cosT, -sinT],
               [sinT,  cosT]])

    pos[1:] = map(float, matmul(A,B))

    return pos

def rotateY(pos, cosT, sinT):

    A = array([pos[0],pos[2]])

    B = array([[cosT, -sinT],
               [sinT,  cosT]])

    pos[0],pos[2] = map(float, matmul(A,B))

    return pos

#projection---------------------------------------------------------------------

def project(pos, f=256):
    xProj = int(f*pos[0] / (f+pos[2]))
    yProj = int(f*pos[1] / (f+pos[2]))

    return xProj, yProj

#rasterisation------------------------------------------------------------------

def rasterise(x0,y0, x1,y1, x2,y2, colour):

    if (x1-x0)*(y2-y0) - (y1-y0)*(x2-x0) >= 0:
        return

    t.color(colour)

    minX = min(x0,x1,x2)
    minY = min(y0,y1,y2)
    maxX = max(x0,x1,x2) + 1
    maxY = max(y0,y1,y2) + 1

    dy01 = y1-y0
    dx01 = x1-x0
    dy12 = y2-y1
    dx12 = x2-x1
    dy20 = y0-y2
    dx20 = x0-x2

    OLDedge01 = dy01*(minX-x0) - (minY-y0)*dx01
    OLDedge12 = dy12*(minX-x1) - (minY-y1)*dx12
    OLDedge20 = dy20*(minX-x2) - (minY-y2)*dx20

    for y in range(minY,maxY):

        edge01 = OLDedge01
        edge12 = OLDedge12
        edge20 = OLDedge20

        for x in range(minX,maxX):

            if edge01 >= 0 and edge12 >= 0 and edge20 >= 0:
                t.goto(x*10,y*10)
                t.stamp()

            edge01 += dy01
            edge12 += dy12
            edge20 += dy20

        OLDedge01 -= dx01
        OLDedge12 -= dx12
        OLDedge20 -= dx20

#-------------------------------------------------------------------------------

def block(x,y,z, cosyaw,sinyaw, cospitch,sinpitch, blockSize=20):

    x *= blockSize
    y *= blockSize
    z *= blockSize

    corner0 = [x, y, z]
    corner1 = [x, y+blockSize, z]
    corner2 = [x+blockSize, corner1[1], z]
    corner3 = [corner2[0], y, z]
    corner4 = [x, y, z+blockSize]
    corner5 = [x, corner1[1], corner4[2]]
    corner6 = [corner2[0], corner1[1], corner4[2]]
    corner7 = [corner2[0], y, corner4[2]]

    if acos(cosyaw) != 0:
        corner0 = rotateY(corner0, cosyaw,sinyaw)
        corner1 = rotateY(corner1, cosyaw,sinyaw)
        corner2 = rotateY(corner2, cosyaw,sinyaw)
        corner3 = rotateY(corner3, cosyaw,sinyaw)
        corner4 = rotateY(corner4, cosyaw,sinyaw)
        corner5 = rotateY(corner5, cosyaw,sinyaw)
        corner6 = rotateY(corner6, cosyaw,sinyaw)
        corner7 = rotateY(corner7, cosyaw,sinyaw)
    if acos(cospitch) != 0:
        corner0 = rotateX(corner0, cospitch,sinpitch)
        corner1 = rotateX(corner1, cospitch,sinpitch)
        corner2 = rotateX(corner2, cospitch,sinpitch)
        corner3 = rotateX(corner3, cospitch,sinpitch)
        corner4 = rotateX(corner4, cospitch,sinpitch)
        corner5 = rotateX(corner5, cospitch,sinpitch)
        corner6 = rotateX(corner6, cospitch,sinpitch)
        corner7 = rotateX(corner7, cospitch,sinpitch)

    corner0x, corner0y = project(corner0)
    corner1x, corner1y = project(corner1)
    corner2x, corner2y = project(corner2)
    corner3x, corner3y = project(corner3)
    corner4x, corner4y = project(corner4)
    corner5x, corner5y = project(corner5)
    corner6x, corner6y = project(corner6)
    corner7x, corner7y = project(corner7)

    rasterise(corner0x,corner0y,corner1x,corner1y,corner2x,corner2y,"red")
    rasterise(corner0x,corner0y,corner2x,corner2y,corner3x,corner3y,"red")
    rasterise(corner4x,corner4y,corner5x,corner5y,corner1x,corner1y,"orange")
    rasterise(corner4x,corner4y,corner1x,corner1y,corner0x,corner0y,"orange")
    rasterise(corner3x,corner3y,corner2x,corner2y,corner6x,corner6y,"yellow")
    rasterise(corner3x,corner3y,corner6x,corner6y,corner7x,corner7y,"yellow")
    rasterise(corner5x,corner5y,corner4x,corner4y,corner7x,corner7y,"green")
    rasterise(corner5x,corner5y,corner7x,corner7y,corner6x,corner6y,"green")
    rasterise(corner4x,corner4y,corner0x,corner0y,corner3x,corner3y,"pink")
    rasterise(corner4x,corner4y,corner3x,corner3y,corner7x,corner7y,"pink")
    rasterise(corner1x,corner1y,corner5x,corner5y,corner6x,corner6y,"purple")
    rasterise(corner1x,corner1y,corner6x,corner6y,corner2x,corner2y,"purple")

import pyautogui
from keyboard import is_pressed
import turtle

from math import sin, cos

from NEAlogicLayer import *

screenWidth, screenHeight = pyautogui.size()
midX, midY = screenWidth//2, screenHeight//2

escPressed = 0
yaw = 0
pitch = 0
sensitivity = 0.003

def frame():
    global yaw
    global pitch

    if is_pressed("esc"):
        s.bye()
        return

    dx = pyautogui.position().x - midX
    dy = pyautogui.position().y - midY
    yaw += dx * sensitivity
    pitch += dy * sensitivity

    cosyaw, sinyaw = cos(yaw), sin(yaw)
    cospitch, sinpitch = cos(pitch), sin(pitch)

    for Y in range(-2,3):
        for X in range(-2,3):
            block(X,0,Y, cosyaw,sinyaw, cospitch,sinpitch)

    #pyautogui.moveTo(midX,midY)

    s.update()
    s.ontimer(frame,16)
    t.clear()

frame()
s.mainloop()
python graphics python-turtle rasterizing