diff --git a/.tests/Vector3/add.test.lua b/.tests/Vector3/add.test.lua
new file mode 100644
index 0000000..9eb1c6b
--- /dev/null
+++ b/.tests/Vector3/add.test.lua
@@ -0,0 +1,104 @@
+local Vector3 = require("worldeditadditions.utils.vector3")
+
+local function format_map(map)
+	local result = {}
+	for key, value in pairs(map) do
+		table.insert(result, key.."\t"..tostring(value))
+	end
+	return table.concat(result, "\n")
+end
+
+
+describe("Vector3.add", function()
+	it("should add 2 positive vectors", function()
+		local a = Vector3.new(3, 4, 5)
+		local b = Vector3.new(1, 1, 1)
+		assert.are.same(
+			a:add(b),
+			Vector3.new(4, 5, 6)
+		)
+	end)
+	it("should support the add operator", function()
+		local a = Vector3.new(3, 4, 5)
+		local b = Vector3.new(1, 1, 1)
+		assert.are.same(
+			a + b,
+			Vector3.new(4, 5, 6)
+		)
+	end)
+	it("should work with floats", function()
+		local a = Vector3.new(3.5, 4.5, 5.5)
+		local b = Vector3.new(1.1, 1.1, 1.1)
+		assert.are.same(
+			a + b,
+			Vector3.new(4.6, 5.6, 6.6)
+		)
+	end)
+	it("should work with scalar a", function()
+		local a = 2
+		local b = Vector3.new(6, 7, 8)
+		assert.are.same(
+			a + b,
+			Vector3.new(8, 9, 10)
+		)
+	end)
+	it("should work with scalar b", function()
+		local a = Vector3.new(6, 7, 8)
+		local b = 2
+		assert.are.same(
+			a + b,
+			Vector3.new(8, 9, 10)
+		)
+	end)
+	it("should handle negative b", function()
+		local a = Vector3.new(3, 4, 5)
+		local b = Vector3.new(-1, -1, -1)
+		assert.are.same(
+			a + b,
+			Vector3.new(2, 3, 4)
+		)
+	end)
+	it("should handle negative a", function()
+		local a = Vector3.new(-3, -4, -5)
+		local b = Vector3.new(1, 1, 1)
+		assert.are.same(
+			a + b,
+			Vector3.new(-2, -3, -4)
+		)
+	end)
+	it("should handle negative a and b", function()
+		local a = Vector3.new(-3, -4, -5)
+		local b = Vector3.new(-1, -1, -1)
+		assert.are.same(
+			a + b,
+			Vector3.new(-4, -5, -6)
+		)
+	end)
+	it("should throw an error on invalid x", function()
+		assert.has.errors(function()
+			Vector3.new("cheese", 4, 5)
+		end)
+	end)
+	it("should throw an error on invalid y", function()
+		assert.has.errors(function()
+			Vector3.new(4, "cheese", 5)
+		end)
+	end)
+	it("should throw an error on invalid z", function()
+		assert.has.errors(function()
+			Vector3.new(66, 2, "cheese")
+		end)
+	end)
+	it("should return a new Vector3 instance", function()
+		local a = Vector3.new(3, 4, 5)
+		local b = Vector3.new(1, 1, 1)
+		
+		local result = a + b
+		assert.are.same(
+			result,
+			Vector3.new(4, 5, 6)
+		)
+		assert.are_not.equal(result, a)
+		assert.are_not.equal(result, b)
+	end)
+end)
diff --git a/.tests/Vector3/divide.test.lua b/.tests/Vector3/divide.test.lua
new file mode 100644
index 0000000..6095e69
--- /dev/null
+++ b/.tests/Vector3/divide.test.lua
@@ -0,0 +1,80 @@
+local Vector3 = require("worldeditadditions.utils.vector3")
+
+describe("Vector3.divide", function()
+	it("should divide 2 positive vectors", function()
+		local a = Vector3.new(30, 40, 50)
+		local b = Vector3.new(2, 2, 2)
+		assert.are.same(
+			a:divide(b),
+			Vector3.new(15, 20, 25)
+		)
+	end)
+	it("should work with the div alias", function()
+		local a = Vector3.new(30, 40, 50)
+		local b = Vector3.new(2, 2, 2)
+		assert.are.same(
+			a:div(b),
+			Vector3.new(15, 20, 25)
+		)
+	end)
+	it("should work with scalar a", function()
+		local a = 2
+		local b = Vector3.new(12, 14, 16)
+		assert.are.same(
+			a / b,
+			Vector3.new(6, 7, 8)
+		)
+	end)
+	it("should work with scalar b", function()
+		local a = Vector3.new(6, 8, 10)
+		local b = 2
+		assert.are.same(
+			a / b,
+			Vector3.new(3, 4, 5)
+		)
+	end)
+	it("should support the divide operator", function()
+		local a = Vector3.new(10, 12, 14)
+		local b = Vector3.new(2, 3, 2)
+		assert.are.same(
+			a / b,
+			Vector3.new(5, 4, 7)
+		)
+	end)
+	it("should handle negative b", function()
+		local a = Vector3.new(30, 40, 50)
+		local b = Vector3.new(-2, -2, -2)
+		assert.are.same(
+			a / b,
+			Vector3.new(-15, -20, -25)
+		)
+	end)
+	it("should handle negative a", function()
+		local a = Vector3.new(-30, -40, -50)
+		local b = Vector3.new(2, 4, 2)
+		assert.are.same(
+			a / b,
+			Vector3.new(-15, -10, -25)
+		)
+	end)
+	it("should handle negative a and b", function()
+		local a = Vector3.new(-30, -40, -50)
+		local b = Vector3.new(-2, -2, -2)
+		assert.are.same(
+			a / b,
+			Vector3.new(15, 20, 25)
+		)
+	end)
+	it("should return a new Vector3 instance", function()
+		local a = Vector3.new(9, 12, 15)
+		local b = Vector3.new(3, 3, 3)
+		
+		local result = a / b
+		assert.are.same(
+			result,
+			Vector3.new(3, 4, 5)
+		)
+		assert.are_not.equal(result, a)
+		assert.are_not.equal(result, b)
+	end)
+end)
diff --git a/.tests/Vector3/multiply.test.lua b/.tests/Vector3/multiply.test.lua
new file mode 100644
index 0000000..d1a39d3
--- /dev/null
+++ b/.tests/Vector3/multiply.test.lua
@@ -0,0 +1,80 @@
+local Vector3 = require("worldeditadditions.utils.vector3")
+
+describe("Vector3.multiply", function()
+	it("should multiply 2 positive vectors", function()
+		local a = Vector3.new(3, 4, 5)
+		local b = Vector3.new(2, 2, 2)
+		assert.are.same(
+			a:multiply(b),
+			Vector3.new(6, 8, 10)
+		)
+	end)
+	it("should work with the mul alias", function()
+		local a = Vector3.new(3, 4, 5)
+		local b = Vector3.new(2, 2, 2)
+		assert.are.same(
+			a:mul(b),
+			Vector3.new(6, 8, 10)
+		)
+	end)
+	it("should work with scalar a", function()
+		local a = 2
+		local b = Vector3.new(6, 7, 8)
+		assert.are.same(
+			a * b,
+			Vector3.new(12, 14, 16)
+		)
+	end)
+	it("should work with scalar b", function()
+		local a = Vector3.new(6, 7, 8)
+		local b = 2
+		assert.are.same(
+			a * b,
+			Vector3.new(12, 14, 16)
+		)
+	end)
+	it("should support the multiply operator", function()
+		local a = Vector3.new(3, 4, 5)
+		local b = Vector3.new(2, 2, 2)
+		assert.are.same(
+			a * b,
+			Vector3.new(6, 8, 10)
+		)
+	end)
+	it("should handle negative b", function()
+		local a = Vector3.new(3, 4, 5)
+		local b = Vector3.new(-1, -1, -1)
+		assert.are.same(
+			a * b,
+			Vector3.new(-3, -4, -5)
+		)
+	end)
+	it("should handle negative a", function()
+		local a = Vector3.new(-3, -4, -5)
+		local b = Vector3.new(2, 2, 2)
+		assert.are.same(
+			a * b,
+			Vector3.new(-6, -8, -10)
+		)
+	end)
+	it("should handle negative a and b", function()
+		local a = Vector3.new(-3, -4, -5)
+		local b = Vector3.new(-2, -2, -2)
+		assert.are.same(
+			a * b,
+			Vector3.new(6, 8, 10)
+		)
+	end)
+	it("should return a new Vector3 instance", function()
+		local a = Vector3.new(3, 4, 5)
+		local b = Vector3.new(3, 3, 3)
+		
+		local result = a * b
+		assert.are.same(
+			result,
+			Vector3.new(9, 12, 15)
+		)
+		assert.are_not.equal(result, a)
+		assert.are_not.equal(result, b)
+	end)
+end)
diff --git a/.tests/Vector3/new.test.lua b/.tests/Vector3/new.test.lua
new file mode 100644
index 0000000..91c044f
--- /dev/null
+++ b/.tests/Vector3/new.test.lua
@@ -0,0 +1,25 @@
+local Vector3 = require("worldeditadditions.utils.vector3")
+
+describe("Vector3.add", function()
+	it("should create a new Vector3", function()
+		assert.are.same(
+			Vector3.new(3, 4, 5),
+			{ x = 3, y = 4, z = 5 }
+		)
+	end)
+	it("should throw an error on invalid x", function()
+		assert.has.errors(function()
+			Vector3.new("cheese", 4, 5)
+		end)
+	end)
+	it("should throw an error on invalid y", function()
+		assert.has.errors(function()
+			Vector3.new(4, "cheese", 5)
+		end)
+	end)
+	it("should throw an error on invalid z", function()
+		assert.has.errors(function()
+			Vector3.new(66, 2, "cheese")
+		end)
+	end)
+end)
diff --git a/.tests/Vector3/subtract.test.lua b/.tests/Vector3/subtract.test.lua
new file mode 100644
index 0000000..58de460
--- /dev/null
+++ b/.tests/Vector3/subtract.test.lua
@@ -0,0 +1,80 @@
+local Vector3 = require("worldeditadditions.utils.vector3")
+
+describe("Vector3.subtract", function()
+	it("should subtract 2 positive vectors", function()
+		local a = Vector3.new(3, 4, 5)
+		local b = Vector3.new(1, 1, 1)
+		assert.are.same(
+			a:subtract(b),
+			Vector3.new(2, 3,  4)
+		)
+	end)
+	it("should work with the sub alias", function()
+		local a = Vector3.new(3, 4, 5)
+		local b = Vector3.new(1, 1, 1)
+		assert.are.same(
+			a:sub(b),
+			Vector3.new(2, 3,  4)
+		)
+	end)
+	it("should work with scalar a", function()
+		local a = 2
+		local b = Vector3.new(6, 7, 8)
+		assert.are.same(
+			a - b,
+			Vector3.new(4, 5, 6)
+		)
+	end)
+	it("should work with scalar b", function()
+		local a = Vector3.new(6, 7, 8)
+		local b = 2
+		assert.are.same(
+			a - b,
+			Vector3.new(4, 5, 6)
+		)
+	end)
+	it("should support the subtract operator", function()
+		local a = Vector3.new(3, 4, 5)
+		local b = Vector3.new(1, 1, 1)
+		assert.are.same(
+			a - b,
+			Vector3.new(2, 3, 4)
+		)
+	end)
+	it("should handle negative b", function()
+		local a = Vector3.new(3, 4, 5)
+		local b = Vector3.new(-1, -1, -1)
+		assert.are.same(
+			a - b,
+			Vector3.new(4, 5, 6)
+		)
+	end)
+	it("should handle negative a", function()
+		local a = Vector3.new(-3, -4, -5)
+		local b = Vector3.new(1, 1, 1)
+		assert.are.same(
+			a - b,
+			Vector3.new(-4, -5, -6)
+		)
+	end)
+	it("should handle negative a and b", function()
+		local a = Vector3.new(-3, -4, -5)
+		local b = Vector3.new(-1, -1, -1)
+		assert.are.same(
+			a - b,
+			Vector3.new(-2, -3, -4)
+		)
+	end)
+	it("should return a new Vector3 instance", function()
+		local a = Vector3.new(3, 4, 5)
+		local b = Vector3.new(1, 1, 1)
+		
+		local result = a - b
+		assert.are.same(
+			result,
+			Vector3.new(2, 3, 4)
+		)
+		assert.are_not.equal(result, a)
+		assert.are_not.equal(result, b)
+	end)
+end)
diff --git a/.tests/Vector3/tostring.test.lua b/.tests/Vector3/tostring.test.lua
new file mode 100644
index 0000000..d8d3b0c
--- /dev/null
+++ b/.tests/Vector3/tostring.test.lua
@@ -0,0 +1,48 @@
+local Vector3 = require("worldeditadditions.utils.vector3")
+
+local function format_map(map)
+	local result = {}
+	for key, value in pairs(map) do
+		table.insert(result, key.."\t"..tostring(value))
+	end
+	return table.concat(result, "\n")
+end
+
+
+describe("Vector3.__tostring", function()
+	it("should stringify a Vector3", function()
+		local a = Vector3.new(3, 4, 5)
+		assert.are.same(
+			a:__tostring(),
+			"(3, 4, 5)"
+		)
+	end)
+	it("should implicitly stringify a Vector3", function()
+		local a = Vector3.new(3, 4, 5)
+		assert.are.same(
+			tostring(a),
+			"(3, 4, 5)"
+		)
+	end)
+	it("should implicitly stringify another Vector3", function()
+		local a = Vector3.new(55, 77, 22)
+		assert.are.same(
+			tostring(a),
+			"(55, 77, 22)"
+		)
+	end)
+	it("should handle negative numbers", function()
+		local a = Vector3.new(-1, -2, -3)
+		assert.are.same(
+			tostring(a),
+			"(-1, -2, -3)"
+		)
+	end)
+	it("should handle a mix of positive and negative numbers", function()
+		local a = Vector3.new(-7, 2, -99)
+		assert.are.same(
+			tostring(a),
+			"(-7, 2, -99)"
+		)
+	end)
+end)
diff --git a/tests.sh b/tests.sh
index 35bac85..d355f72 100755
--- a/tests.sh
+++ b/tests.sh
@@ -27,7 +27,7 @@ luarocks_root="${PWD}/.luarocks";
 # Setup the lua module path
 eval "$(luarocks --tree "${luarocks_root}" path)";
 
-mode="${1}"; shift;
+mode="${1}"; if [[ "$#" -gt 0 ]]; then shift; fi
 
 run_setup() {
 	log_msg "Installing busted";
diff --git a/worldeditadditions/utils/vector3.lua b/worldeditadditions/utils/vector3.lua
new file mode 100644
index 0000000..7e59715
--- /dev/null
+++ b/worldeditadditions/utils/vector3.lua
@@ -0,0 +1,141 @@
+local Vector3 = {}
+Vector3.__index = Vector3
+
+function Vector3.new(x, y, z)
+	if type(x) ~= "number" then
+		error("Error: Expected number for the value of x, but received argument of type "..type(x)..".")
+	end
+	if type(y) ~= "number" then
+		error("Error: Expected number for the value of y, but received argument of type "..type(y)..".")
+	end
+	if type(z) ~= "number" then
+		error("Error: Expected number for the value of z, but received argument of type "..type(z)..".")
+	end
+	
+	local result = {
+		x = x,
+		y = y,
+		z = z
+	}
+	setmetatable(result, Vector3)
+	return result
+end
+
+--- Adds the specified vectors or numbers together.
+-- Returns the result as a new vector.
+-- If 1 of the inputs is a number and the other a vector, then the number will
+-- be added to each of the components of the vector.
+-- @param	a	Vector3|number	The first item to add.
+-- @param	a	Vector3|number	The second item to add.
+-- @returns	Vector3				The result as a new Vector3 object.
+function Vector3.add(a, b)
+	if type(a) == "number" then
+		return Vector3.new(b.x + a, b.y + a, b.z + a)
+	elseif type(b) == "number" then
+		return Vector3.new(a.x + b, a.y + b, a.z + b)
+	end
+	return Vector3.new(a.x + b.x, a.y + b.y, a.z + b.z)
+end
+
+--- Subtracts the specified vectors or numbers together.
+-- Returns the result as a new vector.
+-- If 1 of the inputs is a number and the other a vector, then the number will
+-- be subtracted to each of the components of the vector.
+-- @param	a	Vector3|number	The first item to subtract.
+-- @param	a	Vector3|number	The second item to subtract.
+-- @returns	Vector3				The result as a new Vector3 object.
+function Vector3.subtract(a, b)
+	if type(a) == "number" then
+		return Vector3.new(b.x - a, b.y - a, b.z - a)
+	elseif type(b) == "number" then
+		return Vector3.new(a.x - b, a.y - b, a.z - b)
+	end
+	return Vector3.new(a.x - b.x, a.y - b.y, a.z - b.z)
+end
+--- Alias for Vector3.subtract.
+function Vector3.sub(a, b) return Vector3.subtract(a, b) end
+
+--- Multiplies the specified vectors or numbers together.
+-- Returns the result as a new vector.
+-- If 1 of the inputs is a number and the other a vector, then the number will
+-- be multiplied to each of the components of the vector.
+-- 
+-- If both of the inputs are vectors, then the components are multiplied
+-- by each other (NOT the cross product). In other words:
+-- a.x * b.x, a.y * b.y, a.z * b.z
+-- 
+-- @param	a	Vector3|number	The first item to multiply.
+-- @param	a	Vector3|number	The second item to multiply.
+-- @returns	Vector3				The result as a new Vector3 object.
+function Vector3.multiply(a, b)
+	if type(a) == "number" then
+		return Vector3.new(b.x * a, b.y * a, b.z * a)
+	elseif type(b) == "number" then
+		return Vector3.new(a.x * b, a.y * b, a.z * b)
+	end
+	return Vector3.new(a.x * b.x, a.y * b.y, a.z * b.z)
+end
+--- Alias for Vector3.multiply.
+function Vector3.mul(a, b) return Vector3.multiply(a, b) end
+
+--- Divides the specified vectors or numbers together.
+-- Returns the result as a new vector.
+-- If 1 of the inputs is a number and the other a vector, then the number will
+-- be divided to each of the components of the vector.
+-- @param	a	Vector3|number	The first item to divide.
+-- @param	a	Vector3|number	The second item to divide.
+-- @returns	Vector3				The result as a new Vector3 object.
+function Vector3.divide(a, b)
+	if type(a) == "number" then
+		return Vector3.new(b.x / a, b.y / a, b.z / a)
+	elseif type(b) == "number" then
+		return Vector3.new(a.x / b, a.y / b, a.z / b)
+	end
+	return Vector3.new(a.x / b.x, a.y / b.y, a.z / b.z)
+end
+--- Alias for Vector3.divide.
+function Vector3.div(a, b) return Vector3.divide(a, b) end
+
+--  ██████  ██████  ███████ ██████   █████  ████████  ██████  ██████
+-- ██    ██ ██   ██ ██      ██   ██ ██   ██    ██    ██    ██ ██   ██
+-- ██    ██ ██████  █████   ██████  ███████    ██    ██    ██ ██████
+-- ██    ██ ██      ██      ██   ██ ██   ██    ██    ██    ██ ██   ██
+--  ██████  ██      ███████ ██   ██ ██   ██    ██     ██████  ██   ██
+-- 
+--  ██████  ██    ██ ███████ ██████  ██████  ██ ██████  ███████ ███████
+-- ██    ██ ██    ██ ██      ██   ██ ██   ██ ██ ██   ██ ██      ██
+-- ██    ██ ██    ██ █████   ██████  ██████  ██ ██   ██ █████   ███████
+-- ██    ██  ██  ██  ██      ██   ██ ██   ██ ██ ██   ██ ██           ██
+--  ██████    ████   ███████ ██   ██ ██   ██ ██ ██████  ███████ ███████
+
+function Vector3.__call(x, y, z)
+	return Vector3.new(x, y, z)
+end
+function Vector3.__add(a, b)
+	return Vector3.add(a, b)
+end
+
+function Vector3.__sub(a, b)
+	return Vector3.sub(a, b)
+end
+
+function Vector3.__mul(a, b)
+	return Vector3.mul(a, b)
+end
+
+function Vector3.__div(a, b)
+	return Vector3.divide(a, b)
+end
+
+--- Returns the current Vector3 as a string.
+function Vector3.__tostring(a)
+	return "("..a.x..", "..a.y..", "..a.z..")"
+end
+
+
+
+if worldeditadditions then
+	worldeditadditions.Vector3 = Vector3
+else
+	return Vector3
+end
