diff --git a/pixelgl/vertex.go b/pixelgl/vertex.go
index 0ab8e80b18de79b5a0bb8a28f259d747fa399c1c..64c4d6e4eb7d49645a8b68dced58036408065231 100644
--- a/pixelgl/vertex.go
+++ b/pixelgl/vertex.go
@@ -190,114 +190,95 @@ func (va *VertexArray) SetIndices(indices []int) {
 	})
 }
 
-// SetVertex sets the value of all attributes of a vertex.
-// Argument data must be a slice/array containing the new vertex data.
-func (va *VertexArray) SetVertex(vertex int, data interface{}) {
-	if vertex < 0 || vertex >= va.vertexNum {
-		panic("set vertex error: invalid vertex index")
-	}
-	DoNoBlock(func() {
-		gl.BindBuffer(gl.ARRAY_BUFFER, va.vbo)
-
-		offset := va.stride * vertex
-		gl.BufferSubData(gl.ARRAY_BUFFER, offset, va.format.Size(), gl.Ptr(data))
-
-		gl.BindBuffer(gl.ARRAY_BUFFER, 0)
-	})
-}
-
-func (va *VertexArray) checkVertex(vertex int) {
+// SetVertexAttr sets the value of the specified vertex attribute of the specified vertex.
+//
+// If the vertex attribute does not exist, this method returns false. If the vertex is out of range,
+// this method panics.
+//
+// Supplied value must correspond to the type of the attribute. Correct types are these (righ-hand is the type of value):
+//   Attr{Type: Float}: float32
+//   Attr{Type: Vec2}:  mgl32.Vec2
+//   Attr{Type: Vec3}:  mgl32.Vec3
+//   Attr{Type: Vec4}:  mgl32.Vec4
+// No other types are supported.
+func (va *VertexArray) SetVertexAttr(vertex int, attr Attr, value interface{}) (ok bool) {
 	if vertex < 0 || vertex >= va.vertexNum {
-		panic("invalid vertex index")
+		panic("set vertex attr: invalid vertex index")
 	}
-}
 
-// SetVertexAttributeFloat sets the value of a specified vertex attribute Attr{Purpose: purpose, Type: Float} of type Float
-// of the specified vertex.
-//
-// This function returns false if the specified vertex attribute does not exist. Note that the function panics if
-// the vertex if out of range.
-func (va *VertexArray) SetVertexAttributeFloat(vertex int, purpose AttrPurpose, value float32) (ok bool) {
-	va.checkVertex(vertex)
-	attr := Attr{Purpose: purpose, Type: Float}
 	if _, ok := va.attrs[attr]; !ok {
 		return false
 	}
-	DoNoBlock(func() {
-		gl.BindBuffer(gl.ARRAY_BUFFER, va.vbo)
 
-		offset := va.stride*vertex + va.attrs[attr]
-		gl.BufferSubData(gl.ARRAY_BUFFER, offset, attr.Type.Size(), unsafe.Pointer(&value))
-
-		gl.BindBuffer(gl.ARRAY_BUFFER, 0)
-	})
-	return true
-}
-
-// SetVertexAttributeVec2 sets the value of a specified vertex attribute Attr{Purpose: purpose, Type: Vec2} of type Vec2
-// of the specified vertex.
-//
-// This function returns false if the specified vertex attribute does not exist. Note that the function panics if
-// the vertex if out of range.
-func (va *VertexArray) SetVertexAttributeVec2(vertex int, purpose AttrPurpose, value mgl32.Vec2) (ok bool) {
-	va.checkVertex(vertex)
-	attr := Attr{Purpose: purpose, Type: Vec2}
-	if _, ok := va.attrs[attr]; !ok {
-		return false
-	}
 	DoNoBlock(func() {
 		gl.BindBuffer(gl.ARRAY_BUFFER, va.vbo)
 
 		offset := va.stride*vertex + va.attrs[attr]
-		gl.BufferSubData(gl.ARRAY_BUFFER, offset, attr.Type.Size(), unsafe.Pointer(&value))
+
+		switch attr.Type {
+		case Float:
+			value := value.(float32)
+			gl.BufferSubData(gl.ARRAY_BUFFER, offset, attr.Type.Size(), unsafe.Pointer(&value))
+		case Vec2:
+			value := value.(mgl32.Vec2)
+			gl.BufferSubData(gl.ARRAY_BUFFER, offset, attr.Type.Size(), unsafe.Pointer(&value))
+		case Vec3:
+			value := value.(mgl32.Vec3)
+			gl.BufferSubData(gl.ARRAY_BUFFER, offset, attr.Type.Size(), unsafe.Pointer(&value))
+		case Vec4:
+			value := value.(mgl32.Vec4)
+			gl.BufferSubData(gl.ARRAY_BUFFER, offset, attr.Type.Size(), unsafe.Pointer(&value))
+		}
 
 		gl.BindBuffer(gl.ARRAY_BUFFER, 0)
 	})
+
 	return true
 }
 
-// SetVertexAttributeVec3 sets the value of a specified vertex attribute Attr{Purpose: purpose, Type: Vec3} of type Vec3
-// of the specified vertex.
+// VertexAttr returns the current value of the specified vertex attribute of the specified vertex.
 //
-// This function returns false if the specified vertex attribute does not exist. Note that the function panics if
-// the vertex if out of range.
-func (va *VertexArray) SetVertexAttributeVec3(vertex int, purpose AttrPurpose, value mgl32.Vec3) (ok bool) {
-	va.checkVertex(vertex)
-	attr := Attr{Purpose: purpose, Type: Vec3}
-	if _, ok := va.attrs[attr]; !ok {
-		return false
+// If the vertex attribute does not exist, this method returns nil and false. If the vertex is out of range,
+// this method panics.
+//
+// The type of the returned value follows the same rules as with SetVertexAttr.
+func (va *VertexArray) VertexAttr(vertex int, attr Attr) (value interface{}, ok bool) {
+	if vertex < 0 || vertex >= va.vertexNum {
+		panic("vertex attr: invalid vertex index")
 	}
-	DoNoBlock(func() {
-		gl.BindBuffer(gl.ARRAY_BUFFER, va.vbo)
-
-		offset := va.stride*vertex + va.attrs[attr]
-		gl.BufferSubData(gl.ARRAY_BUFFER, offset, attr.Type.Size(), unsafe.Pointer(&value))
 
-		gl.BindBuffer(gl.ARRAY_BUFFER, 0)
-	})
-	return true
-}
-
-// SetVertexAttributeVec4 sets the value of a specified vertex attribute Attr{Purpose: purpose, Type: Vec4} of type Vec4
-// of the specified vertex.
-//
-// This function returns false if the specified vertex attribute does not exist. Note that the function panics if
-// the vertex if out of range.
-func (va *VertexArray) SetVertexAttributeVec4(vertex int, purpose AttrPurpose, value mgl32.Vec4) (ok bool) {
-	va.checkVertex(vertex)
-	attr := Attr{Purpose: purpose, Type: Vec4}
 	if _, ok := va.attrs[attr]; !ok {
-		return false
+		return nil, false
 	}
+
 	DoNoBlock(func() {
 		gl.BindBuffer(gl.ARRAY_BUFFER, va.vbo)
 
 		offset := va.stride*vertex + va.attrs[attr]
-		gl.BufferSubData(gl.ARRAY_BUFFER, offset, attr.Type.Size(), unsafe.Pointer(&value))
+
+		switch attr.Type {
+		case Float:
+			var data float32
+			gl.GetBufferSubData(gl.ARRAY_BUFFER, offset, attr.Type.Size(), unsafe.Pointer(&data))
+			value = data
+		case Vec2:
+			var data mgl32.Vec2
+			gl.GetBufferSubData(gl.ARRAY_BUFFER, offset, attr.Type.Size(), unsafe.Pointer(&data))
+			value = data
+		case Vec3:
+			var data mgl32.Vec3
+			gl.GetBufferSubData(gl.ARRAY_BUFFER, offset, attr.Type.Size(), unsafe.Pointer(&data))
+			value = data
+		case Vec4:
+			var data mgl32.Vec4
+			gl.GetBufferSubData(gl.ARRAY_BUFFER, offset, attr.Type.Size(), unsafe.Pointer(&data))
+			value = data
+		}
 
 		gl.BindBuffer(gl.ARRAY_BUFFER, 0)
 	})
-	return true
+
+	return value, true
 }
 
 // Do binds a vertex arrray and it's associated vertex buffer, executes sub, and unbinds the vertex array and it's vertex buffer.