Virtual screen

Solar2D virtual screen

One to rule them all

Note: I originally posted this post to This version is slightly modified.

Since your video game can run on several devices with different resolution, width / height ratio and pixel density, a solution had to be found so that the same program could function optimally on all of them. these devices.
Solar2D therefore offers different modes to manage the resizing. While these modes will ultimately make your life easier, at first it can be difficult to understand how they work and where objects are positioned on the screen.
I'll explain it all to you here.

Bonjour Monde !

When testing a language for the first time it is customary to try to write "Hello World" on the screen. It is a ritual indicating that one has succeeded in writing a simple first program that works. With Solar2D we can do this using the display.newText function which will do the job:

local hello = display.newText( "Hello World", 0, 0, native.systemFont, 16 )

Which gives in the simulator ...

Hello World1

Since we have specified a position (0, 0), we expect the text to be at the top left of the screen as is usually the case with most programming languages.
Otherwise it is not, half of the sentence is missing and there is a black band at the top of the screen. Understanding what is happening is not easy because several phenomena come into play to explain the more than strange positioning of our text!
After having struggled a bit, I finally understood how it works!

Positioning of objects.

The first thing to understand with Solar2D is that the objects are positioned relative to their center!
That is, the coordinates provided in our display.newText command do not represent the top left point of the text, but its center. It should also be noted that the famous center will depend on the size of the text to be written!
A graphic object is always enclosed in an invisible rectangle. When we position a graphic object on the screen, it is in fact this rectangle that we place by manipulating it by its anchor.

Graphic Object

In most development environments, the anchor will be at the top left (the yellow dot in the diagram above), but with Solar2D, the anchor is in the center (red dot).
We therefore understand why giving an x coordinate to 0 to our display.newText object placed half of the object outside the screen on the left since it is the red point that is aligned on the edge.

It is not possible to simply work around this problem because the size of the text can vary depending on whether the text is longer or shorter, and therefore its overflow too.
We would need to know the dimension of the graphic object before requesting its layout in order to add to the requested position half of its width and half of its height, but this information will only be available after calling the display.newText function.

It will therefore be necessary to correct the position after the call using the width andheight properties of the hello object

local hello = display.newText("Hello Wolrd", 0, 0, native.systemFont, 16)
hello.x = hello.x + hello.width / 2
hello.y = hello.y + hello.height / 2

Hello World

This time, miraculously, our text is aligned to the left, at the edge of the screen but still not at the top. Correcting the value of the y property even worsened the problem since we increased its value and therefore made the object go down. The upper black band is even wider.

Note : The performance is not impacted by this code. The text object will not be drawn twice. It should be understood that Solar2D manages for you the moment when it is necessary to draw the objects on the screen. The display.NewText command does not perform any plotting, it just adds an object to the list of objects to be plotted. In fact, modifying hello.x andhello.y a posteriori has a very limited performance impact!

The problem no longer comes from the positioning of the Text object but from the scaling mode of Solar2D ... Explanations ...

Scaling Solar2D

Solar2D is a multi-platform development environment. This implies that a Solar2D application must be able to operate on several devices, each possibly having a screen of different resolution.
Therefore it becomes more complicated to produce a compatible application for all these devices when one of them is larger than the other, more elongated or, on the contrary, offers an almost square screen!
This is where Solar2D comes in to try to simplify our work by offering different options to solve this problem.

The virtual screen

To begin with, Solar2D will offer you to work in a virtual screen with a particular resolution that you will define yourself.
The properties setting for this screen can be found in the configuration file config.lua which is found in each of the Solar2D projects.

Among others, you will find the following 4 parameters:

        width = 320,
height = 480,
scale = "letterbox",
fps = 60,

width andheight define the size of the Solar2D virtual screen.
By default the virtual screen has a resolution of 320 * 480 (in 3/2 format therefore!). This more or less means that point (0, 0) is the highest left point of the screen while point (319, 479) will be the lowest right point.
Except that it is not quite that simple!
Because in addition to the size of the virtual screen you have the scale property which will define the type of deformation that Solar2D will apply to make the virtual screen fit into the physical screen of the device!

To understand how the scale property of theconfig.lua file works, let's imagine that we want to port Super Mario Bros U on mobile and use Solar2D for that (yeah ...)
The screen of the WiiU not being in the same proportions (and especially ratio) as our mobile phone, Nintendo defined a virtual screen in the format of the WiiU. it therefore sets width to 1280 and the value ofheight to 720 then tries the different possible values of scale to see what happens!


zoomStretch is the simplest scale value, it takes the virtual screen and distorts it to fit the screen perfectly.
Everything is visible, and nothing is missing, but the image is noticeably overwritten, and the more the screen will have a pronounced ratio, the more the image will be overwritten!
Not sure that we can be satisfied with this little detail!


Note : In this mode the point (0, 0) of the virtual screen corresponds to the left / left corner of the screen. Our "Hello World" will therefore be displayed in the corner of the screen but the text will be distorted. It will be a little taller than it is wide (in portrait mode)!


The default mode when creating a new Solar2D project is letterbox format. This mode guarantees that the image is not distorted (the aspect ratio remains constant) and that the image will be fully visible on the screen. If the terminal does not have exactly the same ratio as the image that we want to display then parts will not be covered by pixels of the image.
Because it is not because the bands are currently black that it is not possible to go and draw in them. For that it would be necessary to draw with negative coordinates in order to exit the virtual screen! It is not infeasible. The interest here is that all the game area is visible and that eventually we can have more!
We notice in any case that the upper left corner of the virtual screen is no longer located on the upper left corner of the screen!


Note : This is exactly what happens in our "Hello World" example. The working area being defined with a 2/3 ratio (320 * 480) and our smartphone having a 10/16 ratio, the upper left corner of coordinates (0, 0) does not coincide with the top / left corner of the screen.


the zoomEven mode will magnify the virtual screen until it fills the larger side. The other axis will overflow him.
A significant portion of our Super Mario is no longer displayed as scores!
In fact this mode would not be bad either for our "Super Mario Bros U" if we lowered the scores a little (which it is entirely possible to do).
the zoomEven mode is mainly used if the user can freely zoom in and out the screen with his fingers. For example Clash of Clan will probably exploit this mode (well if it was programmed in Solar2D )!


Note : in portrait mode, our "Hello World" will be displayed at the top of the screen but will exit to the left since here too the point (0, 0) of the virtual screen does not correspond to the corner left / left of the screen


This particular mode does not lend itself to our example.
The adaptive mode is quite complicated to handle. It will try to keep an identical physical dimension whatever the device.
Imagine that we want Mario to measure exactly 2cm high that it is displayed on a smartphone, a tablet or projected in a movie theater 🙄.
In this case, it is the adaptive mode that should be used!

not defined

Finally there is still a little documented mode, it is the undefined mode.
If you remove the scale property from the config.lua file, Solar2D will not do any transformation.
The width andheight values of the virtual screen will not be taken into account. The virtual screen will do exactly the resolution of your device. Solar2D will not try to make any transformation during the drawing.

Scale undefined

Finally, it seems that this mode (or lack of mode) is exactly what we hoped to have at the start.
A mode where the point (0, 0) is located at the top left of the screen and allowing to display a text in no way distorted!
The only problem is that it is then the developer's responsibility to manage the different resolutions of the multiple devices on which the application could run!
The text will be displayed in a different size depending on the resolution of the device. On a recent smartphone the text will be written very small because of the large number of pixels while on an older smartphone the text will be much larger!

Now that we've seen that Solar2D can help deal with the different screen sizes issue, I doubt you want to deal with that manually, let alone the fact that you end up doing exactly what you want to do. 'one of the zoomStretch,letterbox or zoomEven modes does it for you!

Write at the top left whatever the mode chosen.

We are almost there !
You are now convinced (well I hope) that you should use one of the modes offered by Solar2D .
But whatever the mode chosen, you would still want to write at the top left of the screen because one day, it is no longer "Hello World" that you will want to display but the score of your player, and that is exactly where you want to put it!

Well it's possible !

Solar2D gives you access to several precomputed values

The values returned by display.contentWidth anddisplay.contentHeight are the dimensions of the virtual screen. These are the width andheight values of the config.lua file, whiledisplay.actualContentWidth and display.actualContentHeight are the values of the dimension of the physical screen placed in the frame of the virtual screen!


  • If display.actualContentWidth is greater than display.contentWidth then the virtual screen overflows from the physical screen on the x axis
  • If display.actualContentWidth is equal to display.contentWidth then the virtual screen corresponds perfectly to the physical screen
  • If display.actualContentWidth is less than display.contentWidth then the virtual screen is smaller and there are black bands on the x-axis

Obviously, we have the same properties on the y axis.

It therefore becomes possible to calculate the offset (the number of pixels) allowing to go from the point (0, 0) of the virtual screen to the point (0, 0) of the physical screen.

local OffsetX = (display.contentWidth - display.actualContentWidth) / 2;
local OffsetY = (display.contentHeight - display.actualContentHeight) / 2;

The variables OffsetX andOffsetY are exactly equal to the offset that exists in the two screens!

Our "Hello World" program can therefore be corrected to display our text in the top left corner of the screen, whatever the mode chosen!

local OffsetX = (display.contentWidth - display.actualContentWidth) / 2;
local OffsetY = (display.contentHeight - display.actualContentHeight) / 2;

local hello = display.newText(Hello World" , 0, 0, native.systemFont, 16)
hello.x = 0 + OffsetX + hello.width / 2
hello.y = 0 + OffsetY + hello.height / 2

Hello World

Remark ; OffsetX andOffsetY are floating point values. In addition, Solar2D has already calculated these values for you and rounded them off to make them integers. You can get these values via display.screenOriginX anddisplay.screenOriginY. Using integers rather than floating point numbers is significantly faster but also less precise!


To conclude with the Solar2D positioning and scaling system. Let's say that at the beginning, we pull our hair out a bit to understand, but in the end, once everything has become clear, we realize that the choices made by Solar2D are rather judicious and that anyway it is hardly possible to do better!
To redo your own system would add unnecessary complexity to your program for a result that would be identical at best.


Hillgreen Dream Team

Etienne: DĂ©veloppeur
Manu: Graphiste et développeur
Luis: 3D Graphes et animations
Reynald: Musiques et sons