pygame.font.Font.render() creates a new pygame.Surface containing one line of text. It does not place that text on your window by itself: render the text, obtain a rectangle for positioning, then blit the surface to your destination surface.
The complete render-and-display pattern
A minimal working example follows the sequence documented by Pygame: initialize Pygame, create a display and a font, call render(), position the returned surface with a Rect, blit it, and update the display.
import pygame
pygame.init()
screen = pygame.display.set_mode((640, 360))
font = pygame.font.Font(None, 40)
text_surface = font.render("Hello, Pygame!", True, (255, 255, 255))
text_rect = text_surface.get_rect(center=screen.get_rect().center)
screen.fill((30, 30, 30))
screen.blit(text_surface, text_rect)
pygame.display.flip()
# Keep the window open until it is closed.
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.quit()
The important line is font.render("Hello, Pygame!", True, (255, 255, 255)). The method creates a new surface with the specified text rendered on it. screen.blit() performs the actual drawing operation.
What each argument means
| Argument | Meaning | Example |
|---|---|---|
text |
A single-line string to rasterize. Null characters raise an error. | "Score: 42" |
antialias |
A Boolean. True smooths character edges; False uses a non-antialiased mode. |
True |
color |
The foreground text color, normally an RGB tuple. | (255, 255, 255) |
background |
Optional background color for the text surface. Omit it for transparency outside the glyphs. | (0, 0, 0) or omitted |
The return value is always a Surface sized for the rendered text. An empty string produces a surface with zero width and the font’s height. That surface has nothing visible to draw, so check your input when text appears to be missing.
Free tools Windows power users keep installed
One-click scans. No signup required.
#1 Best Overall
Choosing antialiasing and transparency
Antialiased text
Pass True when you want smoother character edges, especially at ordinary UI sizes or when the text is displayed against a varied background. The resulting surface can use per-pixel alpha.
smooth = font.render("Smooth edges", True, (240, 240, 240))
screen.blit(smooth, (20, 20))
Non-antialiased text
Pass False for a harder, pixel-oriented appearance.
pixel_text = font.render("Pixel style", False, (255, 220, 80))
screen.blit(pixel_text, (20, 60))
With antialiasing disabled, Pygame uses an 8-bit two-color palette. Choose the mode based on the visual style you need rather than assuming one is universally faster or better.
Transparent versus solid backgrounds
If you leave background out, pixels outside the glyphs are transparent, allowing the text to sit over an already-painted scene.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →overlay = font.render("Transparent background", True, (255, 255, 255))
screen.blit(overlay, (20, 100))
Supplying a background color creates a solid text rectangle. When the destination always has that known solid background, Pygame can use colorkey transparency instead of alpha values, which may improve performance.
label = font.render("Solid label", True, (20, 20, 20), (220, 220, 220))
screen.blit(label, (20, 140))
Positioning, anchoring, and centering
render() creates pixels but does not select their location. The returned surface provides get_rect(), a Rect whose attributes can be used as anchors.
Center text in the window
surface = font.render("Centered", True, (255, 255, 255))
rect = surface.get_rect(center=screen.get_rect().center)
screen.blit(surface, rect)
The outer screen.get_rect() supplies the window’s center. Assigning that point to the text rectangle’s center keeps the text centered regardless of its width.
Place text at a corner
surface = font.render("Top left", True, (255, 255, 255))
rect = surface.get_rect(topleft=(16, 16))
screen.blit(surface, rect)
Other useful anchors include topright, midtop, midleft, midright, bottomleft, and bottomright. Anchoring with a rectangle is safer than guessing a text width in pixels.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsCenter on a known horizontal coordinate
title = font.render("Game title", True, (255, 255, 255))
title_rect = title.get_rect(centerx=screen.get_width() // 2, y=12)
screen.blit(title, title_rect)
This centers the title horizontally while keeping its top edge 12 pixels from the top of the display.
Rendering multiple lines
Font.render() renders one line. A newline character is not laid out as a line break; Pygame renders it as an unknown character instead. Split the message and render each line separately.
message = "First linenSecond linenThird line"
lines = message.splitlines()
y = 20
for line in lines:
line_surface = font.render(line, True, (255, 255, 255))
screen.blit(line_surface, (20, y))
y += font.get_linesize()
font.get_linesize() gives consistent vertical spacing based on the font. You can instead advance by each rendered surface’s height when you want spacing tied to the actual result.
y = 20
for line in message.splitlines():
line_surface = font.render(line, True, (255, 255, 255))
screen.blit(line_surface, (20, y))
y += line_surface.get_height()
Center a block of lines
lines = ["Ready", "Press a key", "to begin"]
line_surfaces = [font.render(line, True, (255, 255, 255)) for line in lines]
line_height = font.get_linesize()
block_height = line_height * len(line_surfaces)
start_y = (screen.get_height() - block_height) // 2
for index, line_surface in enumerate(line_surfaces):
rect = line_surface.get_rect(centerx=screen.get_width() // 2,
y=start_y + index * line_height)
screen.blit(line_surface, rect)
This is application-level layout: render() supplies each line image, while your loop determines wrapping, spacing, and alignment.
Rendering in a game loop without unnecessary work
Rendering allocates a new surface. If a label’s text, font, color, and background do not change, render it once and reuse the surface on every frame.
score_surface = font.render("Score: 0", True, (255, 255, 255))
score_rect = score_surface.get_rect(topright=(620, 16))
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill((30, 30, 30))
screen.blit(score_surface, score_rect)
pygame.display.flip()
When the text changes, replace the cached surface and recalculate its rectangle because the new string can have a different width.
score = 42
score_surface = font.render(f"Score: {score}", True, (255, 255, 255))
score_rect = score_surface.get_rect(topright=(620, 16))
For a destination with a solid known background, the optional background argument can avoid per-pixel alpha handling. For transparent overlays, omit it and retain the alpha-based result.
Rank #4
Font objects and reusable styles
Create a Font object for each size or style you need, then reuse it. A font object controls the rendered height and line spacing used by render().
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallheading_font = pygame.font.Font(None, 48)
body_font = pygame.font.Font(None, 28)
heading = heading_font.render("Settings", True, (255, 255, 255))
body = body_font.render("Volume: 80%", True, (200, 200, 200))
Keep the color and background decisions with the visual component that owns the label. This makes it clear which surfaces are transparent overlays and which are solid labels.
When pygame.freetype is a better fit
| API | Return or drawing behavior | Use it when |
|---|---|---|
pygame.font.Font.render |
Returns one text Surface; your code blits it. |
You want the standard Pygame font workflow. |
pygame.freetype.Font.render |
Returns a (Surface, Rect) tuple. |
You want the bounding rectangle together with the rendered result. |
pygame.freetype.Font.render_to |
Renders directly onto an existing surface. | You prefer a direct-to-surface call and freetype features. |
The regular pygame.font path is usually simplest: render, get a rectangle, and blit. The freetype alternatives are useful when their return value or direct rendering model matches your layout code better.
Troubleshooting common failures
Nothing appears
- Confirm that you call
screen.blit(text_surface, ...); creating the surface alone does not draw it. - Call
pygame.display.flip()or another display-update function after drawing. - Check that the text color contrasts with the pixels behind it and that your rectangle is inside the window.
- Make sure the string is not empty. An empty string has zero width.
The window closes immediately
A script that reaches its end exits after one frame. Use an event loop, process pygame.QUIT, and keep drawing until the user closes the window, as in the complete example.
Newlines do not create paragraphs
Do not pass a multiline string directly and expect automatic layout. Use splitlines(), render each line, and advance the y-coordinate with font.get_linesize() or a measured height.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Best Value
Text looks jagged
Pass True for the antialias argument. If you deliberately use False, the hard pixel edges are expected.
Text has an unwanted rectangle
Remove the fourth argument or pass background=None so the area outside glyphs remains transparent. A supplied background color intentionally creates a solid rectangle.
A call raises an error for valid-looking text
Inspect the string for a null character. The documented API rejects null characters. Also verify that the second argument is Boolean-like and that the color is a valid color value such as an RGB tuple.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
If your goal is to capture a website rather than draw text inside a Pygame window, ScreenshotNeo returns a screenshot or PDF through one request. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be disabled. Only clean shots are billed, while bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing. The response identifies the result with X-Page-Verdict and X-Billed headers.
See the ScreenshotNeo API documentation for the complete option list. A direct cURL request is:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
The equivalent Python request is:
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
Node.js can call the same endpoint with the supplied query parameters:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
const buffer = Buffer.from(await res.arrayBuffer());
require('fs').writeFileSync('shot.webp', buffer);
ScreenshotNeo also provides an MCP server for AI agents, including Claude, Cursor, and other MCP clients, with take_screenshot, get_page_info, and capture_pdf tools. Its Free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000 screenshots, and every feature is available on every plan. Create a free ScreenshotNeo account.
Quick Recap
Practical checklist
- Initialize Pygame before creating the display and font.
- Create or reuse a
Fontobject. - Call
render(text, antialias, color, background=None). - Use the returned surface’s
get_rect()for reliable positioning. - Blit the surface to the destination and update the display.
- Split multiline content yourself.
- Cache unchanged labels instead of rendering them every frame.
- Choose
pygame.freetypewhen its tuple return orrender_tomethod better suits your code.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

