As strange as it sounds, the performance drain really is on this one line of code right here: > g2d.drawImage(background, 0, 0, this); The reason is that `background` is a rather large image, and the `g2d` is performing a scale and a rotate every time. Enabling OpenGL via `-Dsun.java2d.opengl=True` as [suggested here][1] might help, but I haven't been able to enable it on my machine. Do note as well that [it may be worth just using an existing game engine][2]. That said, the solution to the problem is to take the work out of the `paintComponent` method. If you never will view the background from a different angle, just draw it at this tilt to begin with. Otherwise, you need yet another thread. So there would be three threads: 1. The GUI thread 2. Your game loop thread 3. The rendering thread In a proof-of-concept style, I added another `volatile BufferedImage drawnBackground` as a field, and I added this to the end of your Main constructor: new Thread(() -> { while(true) { if (background == null) continue; // A new BufferedImage to avoid concurrency issues; in this way, // the reference to this.drawnBackground is always a valid image // to be drawn. If we reassign this.drawnBackground = drawnBackground // while paintComponent() is running, that's fine, as it will simply // draw the old image. // It's also important to note that assignment to references // is an atomic operation, but we still have to declare the field // as volatile BufferedImage drawnBackground = new BufferedImage(background.getWidth(), background.getHeight(), background.getType()); Graphics2D g2d = (Graphics2D) drawnBackground.getGraphics(); g2d.setColor(Color.WHITE); g2d.fillRect(0, 0, background.getWidth(), background.getHeight()); float xscale = (float) SCREENWIDTH / WIDTH; float yscale = (float) SCREENHEIGHT / HEIGHT; g2d.scale(xscale, yscale); g2d.rotate(Math.toRadians(45), WIDTH / 2, HEIGHT / 2); g2d.drawImage(background, 0, 0, this); this.drawnBackground = drawnBackground; } }).start(); The `paintComponent` method changes to look like so: public void paintComponent(Graphics g){ super.paintComponent(g); Graphics2D g2d = (Graphics2D) g; AffineTransform plain = g2d.getTransform(); float xscale = (float)SCREENWIDTH/WIDTH; float yscale = (float)SCREENHEIGHT/HEIGHT; g2d.drawImage(drawnBackground, 0, 0, this); g2d.scale(xscale, yscale); g2d.rotate(Math.toRadians(45), WIDTH/2, HEIGHT/2); AffineTransform normal = g2d.getTransform(); // g2d.drawImage(background, 0, 0, this); // rest of code In this way, we move the expensive operation of rendering the image to another thread. Note that that other thread will still be getting around 30 fps instead of 60 fps, but the game itself will be running at a better framerate. In other words, the background image will be updating at closer to 30 fps, while everything else will be faster. [1]: http://stackoverflow.com/a/200493/1896169 [2]: http://stackoverflow.com/a/8019663/1896169