Skip to content

Instantly share code, notes, and snippets.

@jakebesworth
Forked from Leandros/main.lua
Last active August 11, 2026 11:40
Show Gist options
  • Select an option

  • Save jakebesworth/ac09d54cc05690250096f977105a41f8 to your computer and use it in GitHub Desktop.

Select an option

Save jakebesworth/ac09d54cc05690250096f977105a41f8 to your computer and use it in GitHub Desktop.
Love2D 11.X Fix Your Timestep! "Free the physics" 4th approach
--[[
Original Author: https://github.com/Leandros
Updated Author: https://github.com/jakebesworth
MIT License
Copyright (c) 2018 Jake Besworth
Original Gist: https://gist.github.com/Leandros/98624b9b9d9d26df18c4
Love.run 11.X: https://love2d.org/wiki/love.run
Original Article, 4th algorithm: https://gafferongames.com/post/fix_your_timestep/
Forum Discussion: https://love2d.org/forums/viewtopic.php?f=3&t=85166&start=10
Add this code to bottom of main.lua to override love.run() for Love2D 11.X
Tickrate is how many frames your simulation happens per second (Timestep)
Max Frame Skip is how many frames to allow skipped due to lag of simulation outpacing (on slow PCs) tickrate
---]]
-- 1 / Ticks Per Second
local TICK_RATE = 1 / 100
-- How many Frames are allowed to be skipped at once due to lag (no "spiral of death")
local MAX_FRAME_SKIP = 25
-- No configurable framerate cap currently, either max frames CPU can handle (up to 1000), or vsync'd if conf.lua
function love.run()
if love.load then love.load(love.arg.parseGameArguments(arg), arg) end
-- We don't want the first frame's dt to include time taken by love.load.
if love.timer then love.timer.step() end
local lag = 0.0
-- Main loop time.
return function()
-- Process events.
if love.event then
love.event.pump()
for name, a,b,c,d,e,f in love.event.poll() do
if name == "quit" then
if not love.quit or not love.quit() then
return a or 0
end
end
love.handlers[name](a,b,c,d,e,f)
end
end
-- Cap number of Frames that can be skipped so lag doesn't accumulate
if love.timer then lag = math.min(lag + love.timer.step(), TICK_RATE * MAX_FRAME_SKIP) end
while lag >= TICK_RATE do
if love.update then love.update(TICK_RATE) end
lag = lag - TICK_RATE
end
if love.graphics and love.graphics.isActive() then
love.graphics.origin()
love.graphics.clear(love.graphics.getBackgroundColor())
if love.draw then love.draw() end
love.graphics.present()
end
-- Even though we limit tick rate and not frame rate, we might want to cap framerate at 1000 frame rate as mentioned https://love2d.org/forums/viewtopic.php?f=4&t=76998&p=198629&hilit=love.timer.sleep#p160881
if love.timer then love.timer.sleep(0.001) end
end
end
@wolf81

wolf81 commented Aug 11, 2026

Copy link
Copy Markdown

Nice!

I think this can be further improved by sending in alpha value in the draw() function, that can be used to interpolate state between frames.

--[[
    Original Author: https://github.com/Leandros
    Updated Author: https://github.com/jakebesworth
        MIT License
        Copyright (c) 2018 Jake Besworth

    Original Gist: https://gist.github.com/Leandros/98624b9b9d9d26df18c4
    Love.run 11.X: https://love2d.org/wiki/love.run
    Original Article, 4th algorithm: https://gafferongames.com/post/fix_your_timestep/
    Forum Discussion: https://love2d.org/forums/viewtopic.php?f=3&t=85166&start=10

    Add this code to bottom of main.lua to override love.run() for Love2D 11.X
    Tickrate is how many frames your simulation happens per second (Timestep)
    Max Frame Skip is how many frames to allow skipped due to lag of simulation outpacing (on slow PCs) tickrate
---]]

-- 1 / Ticks Per Second
local TICK_RATE = 1 / 100

-- How many Frames are allowed to be skipped at once due to lag (no "spiral of death")
local MAX_FRAME_SKIP = 25

-- No configurable framerate cap currently, either max frames CPU can handle (up to 1000), or vsync'd if conf.lua

function love.run()
    if love.load then love.load(love.arg.parseGameArguments(arg), arg) end

    -- We don't want the first frame's dt to include time taken by love.load.
    if love.timer then love.timer.step() end

    local lag = 0.0

    -- Main loop time.
    return function()
        -- Process events.
        if love.event then
            love.event.pump()
            for name, a, b, c, d, e, f in love.event.poll() do
                if name == "quit" then
                    if not love.quit or not love.quit() then
                        return a or 0
                    end
                end
                love.handlers[name](a, b, c, d, e, f)
            end
        end

        -- Cap number of Frames that can be skipped so lag doesn't accumulate
        if love.timer then lag = math.min(lag + love.timer.step(), TICK_RATE * MAX_FRAME_SKIP) end

        while lag >= TICK_RATE do
            if love.update then love.update(TICK_RATE) end
            lag = lag - TICK_RATE
        end

        local alpha = lag / TICK_RATE  -- Interpolation factor (0 to 1)

        if love.graphics and love.graphics.isActive() then
            love.graphics.origin()
            love.graphics.clear(love.graphics.getBackgroundColor())

            if love.draw then love.draw(alpha) end
            love.graphics.present()
        end

        -- Even though we limit tick rate and not frame rate, we might want to cap framerate at 1000 frame rate as mentioned https://love2d.org/forums/viewtopic.php?f=4&t=76998&p=198629&hilit=love.timer.sleep#p160881
        if love.timer then love.timer.sleep(0.001) end
    end
end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment