TLfres aims to make it easy to make any game run at any screen resolution. Using it doesn't even require you to rewrite your graphics code!

It's under the ZLIB license.


Everything in the module is contained in the TLfres namespace, so don't worry about it messing with your global variables. Assuming your game was built to use the Love-default resolution of 800×600:

  1. Put tlfres.lua in your game's folder
  2. At the top of main.lua, add the line local TLfres = require "tlfres"
  3. At the beginning of love.draw(), add the line TLfres.beginRendering(800, 600)
  4. At the bottom of love.draw() add the line TLfres.endRendering()
  5. Whenever you need to know the current scaling factor, call TLfres.getScale(800, 600)
  6. Whenever you would call love.mouse.getPosition, instead call TLfres.getMousePosition(800, 600)
  7. Whenever you would call, instead call * TLfres.getScale()) (Love doesn't scale point sizes automatically)


Q) Why should I use this instead of
A) Because even stretching the screen to a given res requires tricky math, which TLfres can do for you. Furthermore, most people hate stretched graphics - that's why you should use TLfres's letterboxing option. Instead of stretching the screen, it resizes it in the correct ratio and draws black boxes at the top and bottom if needed.
Q) What resolutions / aspect ratios does it support?
A) Any.
Q) I made my game to use a non-default resolution. Will I have to change everything to make TLfres work with it?
A) Nope! Just call TLfres.beginRendering() with the resolution you built your game for (see below).



TLfres.beginRendering(width, height, centered)

Call this at the very beginning of love.draw(), before drawing anything. This scales and translates the screen coordinates as needed by the current screen resolution. If you want to draw something at absolute screen coordinates, do it before this function (or after endRendering()).

number width
Width of the target canvas.
number height
Width of the target canvas.
boolean centered (false)
If true, (0, 0) is the middle of the canvas, otherwise it is the top-left corner.



Call this at the end of love.draw(), after drawing everything. This draws letterboxes to trim any graphics which should be out of the screen. It defaults to black letterboxes.

table letterboxColor ({0,0,0, 255})
The color of the letterboxes, given in RGBA (just like Defaults to black.


TLfres.getScale(width, height)
number width
The canvas width expected by your code.
number height
The canvas height expected by your code.


TLfres.getMousePosition(width, height)

Use this any time you would normally call love.mouse.getPosition. The returned position is scaled to the given dimensions.

number width
The canvas width expected by your input code.
number height
The canvas height expected by your input code.


local lwGetMode     =
local lgPush        =
local lgPop         =
local lgTranslate   =
local lgScale       =
local lgRectangle   =
local lgSetColor    =
local lmGetPosition =
local min = math.min

local TLfres = {}

local lastMouseX, lastMouseY = 0, 0
local currentlyRendering

-- Internal helper function
local function _getRawMousePosition(width, height)
   local x, y = lmGetPosition()
   local w, h = lwGetMode()
   local scale = min(w/width, h/height)
   return (x - (w - width * scale) * 0.5)/scale, (y - (h - height * scale) * 0.5)/scale

-- Use this any time you would normally call love.mouse.getPosition.
-- The returned position is scaled to the given dimensions.
function TLfres.getMousePosition(width, height)
   local x, y = _getRawMousePosition(width, height)
   if x >= 0 and x <= width and y >= 0 and y <= height then
      lastMouseX, lastMouseY = _getRawMousePosition(width, height)
   return lastMouseX, lastMouseY

-- Calculate the current scale based on the desired dimensions and current ones
-- If called within a rendering block, width and height are optional.
function TLfres.getScale(width, height)
   if currentlyRendering then
      width  = width  or currentlyRendering[1]
      height = height or currentlyRendering[2]
   local w, h = lwGetMode()
   return min(w/width, h/height)

-- Zooms and centers to fit width×height into the current window.
-- 0,0 is at the top-left of the canvas, or the middle if centered is true.
-- Use before this and after done rendering
function TLfres.beginRendering(width, height, centered)
   if currentlyRendering then
      error("Must call tlfres.endRendering before calling beginRendering.")
   currentlyRendering = {width, height}

   local w, h = lwGetMode()
   local scale = min(w/width, h/height)
   lgTranslate((w - width * scale) * 0.5, (h - height * scale) * 0.5)
   if centered then
      lgTranslate(0.5 * width, 0.5 * height)
   return scale

local _black = {0, 0, 0, 255}

-- Pops out of the transform; if letterboxColor is true, draws black letterbox
-- bars. letterboxColor can also be any {r, g, b, a} table.
function TLfres.endRendering(letterboxColor)
   if not currentlyRendering then
      error("Must call tlfres.beginRendering before calling endRendering.")
   local width, height = currentlyRendering[1], currentlyRendering[2]
   currentlyRendering = nil

   local w, h = lwGetMode()
   local scale = min(w/width, h/height)
   width, height = width * scale, height * scale

   lgSetColor(letterboxColor or _black)
   lgRectangle("fill", 0, 0,  w,  0.5 * (h - height)) -- top
   lgRectangle("fill", 0, h,  w, -0.5 * (h - height)) -- bottom
   lgRectangle("fill", 0, 0,  0.5 * (w - width), h)   -- left
   lgRectangle("fill", w, 0, -0.5 * (w - width), h)   -- right

return TLfres


local TLfres = require "tlfres"

local CANVAS_WIDTH = 800
local CANVAS_HEIGHT = 600
local POINT_SIZE = 1

function love.mouse.getPosition() -- Override the standard function with our helper function
   return TLfres.getMousePosition(CANVAS_WIDTH, CANVAS_HEIGHT)

function love.draw()
   tlfres.beginRendering(CANVAS_WIDTH, CANVAS_HEIGHT)*POINT_SIZE) -- Point size doesn't scale automatically, so multiply it manually., 300) -- Will draw at the center of the canvas no matter how the screen is resized.

   tlfres.endRendering() -- Draw black letterbox

Other Languages