diff --git a/punyverse/entity.py b/punyverse/entity.py
index 5f0d4b8..6cfb67c 100644
--- a/punyverse/entity.py
+++ b/punyverse/entity.py
@@ -5,7 +5,7 @@ from pyglet.gl import *
 # noinspection PyUnresolvedReferences
 from six.moves import range
 
-from punyverse.glgeom import compile, sphere, flare, disk, glMatrix, glRestore, belt
+from punyverse.glgeom import compile, flare, disk, glMatrix, glRestore, belt, Sphere
 from punyverse.orbit import KeplerOrbit
 from punyverse.texture import get_best_texture, load_clouds
 
@@ -118,15 +118,20 @@ class Sky(Entity):
         super(Sky, self).__init__('Sky', (0, 0, 0), (pitch, yaw, roll))
         self.world = world
 
-        texture = get_best_texture(info['texture'])
+        self.texture = get_best_texture(info['texture'])
         division = info.get('division', 30)
-        self.sky_id = compile(sphere, info.get('radius', 1000000), division, division, texture,
-                              inside=True, lighting=False)
+        self.sphere = Sphere(info.get('radius', 1000000), division, division)
 
     def draw(self, options):
         cam = self.world.cam
-        with glMatrix((-cam.x, -cam.y, -cam.z), self.rotation), glRestore(GL_CURRENT_BIT):
-            glCallList(self.sky_id)
+        with glMatrix((-cam.x, -cam.y, -cam.z), self.rotation), glRestore(GL_TEXTURE_BIT | GL_ENABLE_BIT):
+            glEnable(GL_CULL_FACE)
+            glEnable(GL_TEXTURE_2D)
+            glDisable(GL_LIGHTING)
+
+            glCullFace(GL_FRONT)
+            glBindTexture(GL_TEXTURE_2D, self.texture)
+            self.sphere.draw()
 
 
 class Body(Entity):
@@ -258,11 +263,11 @@ class SphericalBody(Body):
         division = info.get('division', max(min(int(self.radius / 8), 60), 10))
         self.light_source = info.get('light_source', False)
 
-        texture = get_best_texture(info['texture'])
-        self.sphere_id = compile(sphere, self.radius, division, division, texture)
+        self.texture = get_best_texture(info['texture'])
+        self.sphere = Sphere(self.radius, division, division)
 
         self.atmosphere_id = 0
-        self.cloudmap_id = 0
+        self.clouds = None
         self.corona_id = 0
         self.ring_id = 0
 
@@ -273,8 +278,8 @@ class SphericalBody(Body):
             cloud_texture = atmosphere_data.get('cloud_texture', None)
             corona_texture = atmosphere_data.get('corona_texture', None)
             if cloud_texture is not None:
-                cloud_texture = get_best_texture(cloud_texture, loader=load_clouds)
-                self.cloudmap_id = compile(sphere, self.radius + 2, division, division, cloud_texture, lighting=False)
+                self.cloud_texture = get_best_texture(cloud_texture, loader=load_clouds)
+                self.clouds = Sphere(self.radius + 2, division, division)
 
             if corona_texture is not None:
                 corona = get_best_texture(corona_texture, clamp=True)
@@ -301,11 +306,23 @@ class SphericalBody(Body):
             self.ring_id = compile(disk, distance, distance + size, 30,
                                    get_best_texture(info['ring'].get('texture', None), clamp=True))
 
-    def _draw_sphere(self):
-        with glMatrix(self.location, self.rotation), glRestore(GL_CURRENT_BIT | GL_ENABLE_BIT):
+    def _draw_sphere(self, fv4=GLfloat * 4):
+        with glMatrix(self.location, self.rotation), glRestore(GL_LIGHTING_BIT | GL_ENABLE_BIT | GL_TEXTURE_BIT):
+            glEnable(GL_CULL_FACE)
+            glCullFace(GL_BACK)
+
+            glEnable(GL_TEXTURE_2D)
+            glBindTexture(GL_TEXTURE_2D, self.texture)
+
             if self.light_source:
                 glDisable(GL_LIGHTING)
-            glCallList(self.sphere_id)
+            else:
+                glDisable(GL_BLEND)
+                glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, fv4(1, 1, 1, 0))
+                glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, fv4(1, 1, 1, 0))
+                glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 125)
+
+            self.sphere.draw()
 
     def _draw_atmosphere(self, glMatrixBuffer=GLfloat * 16):
         with glMatrix(self.location), glRestore(GL_ENABLE_BIT | GL_CURRENT_BIT):
@@ -326,10 +343,16 @@ class SphericalBody(Body):
                 glCallList(self.corona_id)
 
     def _draw_clouds(self):
-        with glMatrix(self.location, self.rotation), glRestore(GL_ENABLE_BIT | GL_CURRENT_BIT):
+        with glMatrix(self.location, self.rotation), glRestore(GL_ENABLE_BIT | GL_TEXTURE_BIT):
             glEnable(GL_BLEND)
             glEnable(GL_ALPHA_TEST)
-            glCallList(self.cloudmap_id)
+            glEnable(GL_CULL_FACE)
+            glDisable(GL_LIGHTING)
+            glEnable(GL_TEXTURE_2D)
+
+            glCullFace(GL_BACK)
+            glBindTexture(GL_TEXTURE_2D, self.cloud_texture)
+            self.clouds.draw()
 
     def _draw_rings(self):
         with glMatrix(self.location, self.ring_rotation), glRestore(GL_CURRENT_BIT):
@@ -341,7 +364,7 @@ class SphericalBody(Body):
         if options.atmosphere and (self.atmosphere_id or self.corona_id):
             self._draw_atmosphere()
 
-        if options.cloud and self.cloudmap_id:
+        if options.cloud and self.clouds:
             self._draw_clouds()
 
         if self.ring_id:
diff --git a/punyverse/glgeom.py b/punyverse/glgeom.py
index 79ec413..45d9bbe 100644
--- a/punyverse/glgeom.py
+++ b/punyverse/glgeom.py
@@ -1,3 +1,5 @@
+from array import array
+from ctypes import c_int, c_float, byref, cast, POINTER, c_uint
 from math import *
 from random import random, gauss, choice
 
@@ -7,7 +9,7 @@ from six.moves import range
 
 TWOPI = pi * 2
 
-__all__ = ['compile', 'ortho', 'frustrum', 'crosshair', 'circle', 'disk', 'sphere', 'colourball', 'belt',
+__all__ = ['compile', 'ortho', 'frustrum', 'crosshair', 'circle', 'disk', 'Sphere', 'belt',
            'flare', 'glSection', 'glMatrix', 'glRestore', 'progress_bar']
 
 
@@ -45,6 +47,17 @@ class glRestore(object):
         glPopAttrib()
 
 
+class glRestoreClient(object):
+    def __init__(self, flags):
+        self.flags = flags
+
+    def __enter__(self):
+        glPushClientAttrib(self.flags)
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        glPopClientAttrib()
+
+
 class glMatrix(object):
     def __init__(self, location=None, rotation=None):
         self.location = location
@@ -169,49 +182,57 @@ def flare(rinner, router, res, prob, tex):
                 last_y = y
 
 
-def sphere(r, lats, longs, tex, lighting=True, inside=False, fv4=GLfloat * 4):
-    with glRestore(GL_ENABLE_BIT | GL_TEXTURE_BIT):
-        sphere = gluNewQuadric()
-        gluQuadricDrawStyle(sphere, GLU_FILL)
-        gluQuadricTexture(sphere, True)
-        if lighting:
-            gluQuadricNormals(sphere, GLU_SMOOTH)
-
-        glEnable(GL_CULL_FACE)
-        glCullFace(GL_FRONT if inside else GL_BACK)
-        glEnable(GL_TEXTURE_2D)
-        if lighting:
-            glDisable(GL_BLEND)
-            glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, fv4(1, 1, 1, 0))
-            glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, fv4(1, 1, 1, 0))
-            glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 125)
-        else:
-            glDisable(GL_LIGHTING)
-        glBindTexture(GL_TEXTURE_2D, tex)
-
-        gluSphere(sphere, r, lats, longs)
-
-        gluDeleteQuadric(sphere)
+def array_to_ctypes(arr):
+    return cast(arr.buffer_info()[0], POINTER({
+        'f': c_float,
+        'i': c_int,
+        'I': c_uint,
+    }[arr.typecode]))
 
 
-def colourball(r, lats, longs, colour, fv4=GLfloat * 4):
-    """
-        Sphere function from the OpenGL red book.
-    """
-    with glRestore(GL_ENABLE_BIT):
-        sphere = gluNewQuadric()
+class Sphere(object):
+    def __init__(self, r, lats, longs):
+        tau = pi * 2
+        phi_div = tau / longs
+        theta_div = pi / lats
 
-        glDisable(GL_BLEND)
-        glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, fv4(*colour))
-        glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, fv4(1, 1, 1, 1))
-        glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 125)
-        glEnable(GL_CULL_FACE)
-        glCullFace(GL_BACK)
+        self.vertex_count = (lats + 1) * (longs + 1) * 2
+        buffer = self.vertex_count * 8 * [0]
+        index = 0
+        for i in range(longs + 1):
+            phi1, phi2 = i * phi_div, (i + 1) * phi_div
+            for j in range(lats + 1):
+                theta = j * theta_div
+                sine = sin(theta)
+                dz = cos(theta)
+                t = 1 - theta / pi
+                dx1 = sine * cos(phi2)
+                dy1 = sine * sin(phi2)
+                dx2 = sine * cos(phi1)
+                dy2 = sine * sin(phi1)
+                buffer[index:index + 16] = [r * dx1, r * dy1, r * dz, dx1, dy1, dz, phi2 / tau, t,
+                                            r * dx2, r * dy2, r * dz, dx2, dy2, dz, phi1 / tau, t]
+                index += 16
 
-        gluSphere(sphere, r, lats, longs)
+        vbo = c_uint()
+        glGenBuffers(1, byref(vbo))
+        self.vbo = vbo.value
+        glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
+        buffer = array('f', buffer)
+        glBufferData(GL_ARRAY_BUFFER, buffer.itemsize * len(buffer), array_to_ctypes(buffer),  GL_STATIC_DRAW)
+        glBindBuffer(GL_ARRAY_BUFFER, 0)
 
-        glEnable(GL_BLEND)
-        gluDeleteQuadric(sphere)
+    def draw(self):
+        with glRestoreClient(GL_CLIENT_VERTEX_ARRAY_BIT):
+            glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
+            glEnableClientState(GL_VERTEX_ARRAY)
+            glEnableClientState(GL_NORMAL_ARRAY)
+            glEnableClientState(GL_TEXTURE_COORD_ARRAY)
+            glVertexPointer(3, GL_FLOAT, 32, 0)
+            glNormalPointer(GL_FLOAT, 32, 3 * 4)
+            glTexCoordPointer(3, GL_FLOAT, 32, 6 * 4)
+            glDrawArrays(GL_TRIANGLE_STRIP, 0, self.vertex_count)
+            glBindBuffer(GL_ARRAY_BUFFER, 0)
 
 
 def belt(radius, cross, object, count):
diff --git a/punyverse/world.json b/punyverse/world.json
index 2b73f48..c68a76e 100644
--- a/punyverse/world.json
+++ b/punyverse/world.json
@@ -306,7 +306,7 @@
     "division": 30,
     "pitch": 90,
     "yaw": 30,
-    "roll": 180
+    "roll": -90
   },
   "asteroids": ["asteroids/01.obj", "asteroids/02.obj", "asteroids/03.obj"],
   "start": {