So you are writing a game in PyGame and you come up with the most beautiful particle effect. But when you implement it, you find that the particles seem to jitter between pixels, and it spoils the effect. This is because PyGame can only render sprites at integer coordinates, even if you give it float values. OpenGL or other 3D APIs don't suffer from this because they can apply filtering and effectively render images between pixels (or sub-pixel). But you can't do that with a 2D blitter.

Until now that is! :-) I've written a small module that pre-calculates sub-pixel sprites so that you can render a PyGame surface at fractional coordinates. It's nice and easy to use, and is almost a drop in replacement. Here's an example:

ball = pygame.image.load("ball.png")
ball_subpixel = SubPixelSurface(ball)
x, y = 100.23, 60.58323
screen.blit(, y), (x, y))

It's as fast to blit sub-pixel surfaces as it is to blit regular surfaces, but there is a downside I'm afraid (isn't there always). Because of the pre-calculation, more memory is required to store the sub-pixel surface. The default level of sub-pixel accuracy uses 9X the memory. I recommend using it for small sprites, but not for large images or images that don't move. The code is completely public domain. It requires the Numeric library.


Because of the nature of sub-pixel surfaces you really need to see it moving to appreciate it, but here is a screen-shot of the sample code in the zip.


Update. A few people have commented that a screen shot is pointless. So that you don't have to run the sample code, I have generated an animated gif to demonstrate the effect. The spheres at the the top are sub-pixel, the spheres below that are blitted in the usual manner.

This blog post was posted to It's All Geek to Me on Wednesday April 25th, 2007 at 9:24AM

21 Responses to "Going sub-pixel with PyGame"

  • [...] McGugan has just posted some Pygame code that will allow subpixel rendering, which should allow Pygame developers to render smooth looking graphics using software rendering [...]

  • April 25th, 2007, 3:05 p.m.

    Wow... that is amazing. Do you have any other special effects like this? :-)

  • k0wax
    April 25th, 2007, 5:18 p.m.

    cool. I've tested some sub-pixel drawing with opengl and it work worst in some cases (blur, waves, shape losing, etc). probably your precalculation code can help me ^_^

  • REd
    April 26th, 2007, 4:09 p.m.

    Wow, My eyes must really suck because I cannot tell teh difference between the 2.

  • Zogo
    April 26th, 2007, 5:37 p.m.

    I have a problem trying to run the demo. It is complaining it cannot find the Numeric package. I have search but I can only find the NumPy module. Could you help with this?

  • April 26th, 2007, 5:41 p.m.

    I can indeed.

    Numeric library

  • [...] with PyGame April 26th, 2007 — drawk | Edit Will McGugan posted an article about subpixel manipulation in Python.  This is possible in other technologies like OpenGL and DirectX but Will has implemented it in [...]

  • Zogo
    April 27th, 2007, 7:37 a.m.

    Hi Will,

    Thanks for the link. The problem is that there is no Numeric library for Python 2.5 which I'm using :-(. Any other hint?


  • Zogo
    April 27th, 2007, 8:53 a.m.

    I found a package of the Numeric to use in the Python 2.5.

    Now it is working. Quite impresive effect!!


  • April 27th, 2007, 9:52 a.m.

    Glad you got it working. :-)

    I've uploaded a new version that uses NumPy (which has builds for 2.5) if available, or fall back to Numeric if it isn't.

  • April 27th, 2007, 1:51 p.m.


  • Bastian
    May 8th, 2007, 4:47 p.m.

    Your new version still breaks with numpy on my system: RuntimeWarning: use surfarray: No module named Numeric
    surf_array_rgb[1:ow+1:, 1:oh+1:, ::] = pygame.surfarray.array3d(surface)
    Traceback (most recent call last):
    File "", line 46, in ?
    File "", line 18, in run
    pingball_subpixel = SubPixelSurface(pingball, x_level=4)
    File "D:\pydemo\src\subpixel\", line 43, in __init__
    surf_array_rgb[1:ow+1:, 1:oh+1:, ::] = pygame.surfarray.array3d(surface)
    File "d:\pydemo\lib\python2.4\pygame-1.7.1release-py2.4-win32.egg\pygame\__ini", line 52, in __getattr__
    raise NotImplementedError, MissingPygameModule
    NotImplementedError: surfarray module not available

  • May 8th, 2007, 5:12 p.m.

    Not sure what is happening there. At a guess I would say that the pygame.surfarray module is actualy dependant on Numeric, even if Numpy is used to manipulate the returned array.

    Just checked pygame pygame\, and that does appear to be the case. Try replacing the 'import Numeric' line in there with 'import numpy as Numeric'.

  • Kevin
    June 4th, 2007, 4:13 a.m.

    I've spent some time trying to resolve this error -- which I'll admit may have more to do with pygame.image than the subpixel code, but since I am unable to resolve it, I thought someone here might have some insight.

    Traceback (most recent call last):
    File "/home/kevin/src/python/pygame/subpixel/subpixel/", line 89, in _generate
    pygame_surface = pygame.image.fromstring(rgba_data, a.shape[:2][::-1], "RGBA")
    ValueError: String length does not equal format and resolution size

  • June 4th, 2007, 9:58 a.m.

    Hi Kevin,

    Haven't seen that myself. Could you find the length of rgba_data, and the value of a.shape[:2][::-1].

    And if you could send me a minimal sample, and the bitmap that is causing the problem, I'll take a look at it.

  • [...] escribió un pequeño paquete que toma una superficie (un Surface) de Python y lo transforma en una superficie capaz de dibujar objetos con coordenadas fraccionales, justo como lo harías con OpenGL u otra librería 3D. Al hacer este procedimiento con el código [...]

  • January 26th, 2009, 1:13 a.m.

    Nice job. I have a question, though: would it be possible to extend this to use subpixels like ClearType font rendering does in Windows? I'm wondering because I'm trying to move some fonts around with this library and the fonts just don't look very good without the ability to use the color subpixels as well.

  • Magnet
    February 15th, 2011, 8:41 p.m.

    I thought of another way to use subpixel,

    Enlarge the Surface by a multiple of 10

    Enlarge the Surface to blit by the same above

    multiply the x and y to blit by the same above

    Then blit

    Then shrink by the same above!

  • February 15th, 2011, 9:42 p.m.

    Magnet, you are absolutely correct. That would actually produce high quality antialiasing. But, it would use a lot of cpu and memory to generate…

  • Simon
    December 29th, 2011, 10:21 a.m.

    This is a pretty awesome feature you've added that I've been looking for for a while now. I was about to start using openGL but I think this will let me stick with standard pygame graphics for a while longer now. My biggest problem with this is that the subpixel surfaces don't really support rotation (I'm not sure about other transforms, I'm guessing not). I suppose that's something I could work on myself. Anyways, great job with this.

  • Tyler Troy
    November 27th, 2014, 8:19 p.m.

    Beautiful. Just beautiful. Thank you.

Leave a Comment

You can use bbcode in the comment: e.g. [b]This is bold[/b], [url][/url], [code python]import this[/code]
Preview Posting...
Previewing comment, please wait a moment...

My Tweets

Will McGugan

My name is Will McGugan. I am an unabashed geek, an author, a hacker and a Python expert – amongst other things!

Search for Posts
Possibly related posts
Popular Tags
Recent Comments
Why another framework? what wrong with django, pyramid, flask?will be have answer for this question in the docs)
Hi! Really great code, good work! But trying to use it on a responsive site, it didn't resize images. So, ...
using of a recursion: def thousands_with_commas(i): def _recurse(n): x, y = divmod(n, 1000) if x 1000: return [x, y] return ...
#1 import string from collections import Counter def tagwords(): tagcounter = Counter() with open(tagwords.txt, r) as wordfile: words = list(filter(None, ...
- the jeffster on Python Coder Test
This is because mod_wsgi does not pass OS environment variables to the underlying application by default
© 2008 Will McGugan.

A technoblog blog, design by Will McGugan