diff --git a/doc/lua_api.txt b/doc/lua_api.txt
index ff2143cc841ba07b6fd93075201317bae1e05cae..698efbe8af2d0ad98e7b8485df392ddd55ecc79c 100644
--- a/doc/lua_api.txt
+++ b/doc/lua_api.txt
@@ -1265,6 +1265,12 @@ ColorString
 #RRGGBBAA
 ^ defines a color in hexadecimal format and alpha channel
 
+Named colors are also supported and are equivalent to "CSS Color Module Level 4"
+(http://dev.w3.org/csswg/css-color/#named-colors). To specify the value of the
+alpha channel, append #AA to the end of the color name (e.g. colorname#08). For
+named colors the hexadecimal string representing the alpha value must (always)
+be two hexadecimal digits.
+
 Vector helpers
 ---------------
 vector.new([x[, y, z]]) -> vector
diff --git a/src/util/string.cpp b/src/util/string.cpp
index 57545aa20a710cc5db4547c30c3fc244ff60ca75..c590e7e577a484654861f2d4f94f8587c9399c8d 100644
--- a/src/util/string.cpp
+++ b/src/util/string.cpp
@@ -24,12 +24,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include <sstream>
 #include <iomanip>
+#include <map>
 
 #include "../sha1.h"
 #include "../base64.h"
 #include "../hex.h"
 #include "../porting.h"
 
+static bool parseHexColorString(const std::string &value, video::SColor &color);
+static bool parseNamedColorString(const std::string &value, video::SColor &color);
+
 #ifdef __ANDROID__
 const wchar_t* wide_chars =
 	L" !\"#$%&'()*+,-./0123456789:;<=>?@"
@@ -307,62 +311,269 @@ u64 read_seed(const char *str)
 
 bool parseColorString(const std::string &value, video::SColor &color, bool quiet)
 {
-	const char *hexpattern = NULL;
-	video::SColor outcolor(255, 255, 255, 255);
-
-	if (value[0] == '#') {
-		if (value.size() == 9)
-			hexpattern = "#RRGGBBAA";
-		else if (value.size() == 7)
-			hexpattern = "#RRGGBB";
-		else if (value.size() == 5)
-			hexpattern = "#RGBA";
-		else if (value.size() == 4)
-			hexpattern = "#RGB";
-	}
+	bool success;
 
-	if (!hexpattern)
-		goto fail;
+	if (value[0] == '#')
+		success = parseHexColorString(value, color);
+	else
+		success = parseNamedColorString(value, color);
 
-	assert(strlen(hexpattern) == value.size());
-	for (size_t pos = 0; pos < value.size(); ++pos) {
-		// '#' in the pattern means skip that character
-		if (hexpattern[pos] == '#')
-			continue;
+	if (!success && !quiet)
+		errorstream << "Invalid color: \"" << value << "\"" << std::endl;
+
+	return success;
+}
+
+static bool parseHexColorString(const std::string &value, video::SColor &color)
+{
+	unsigned char components[] = { 0x00, 0x00, 0x00, 0xff }; // R,G,B,A
 
-		// Else assume hexpattern[pos] is one of 'R' 'G' 'B' 'A'
-		// Read one or two digits, depending on hexpattern
-		unsigned char c1, c2;
-		if (hexpattern[pos+1] == hexpattern[pos]) {
-			// Two digits, e.g. hexpattern == "#RRGGBB"
-			if (!hex_digit_decode(value[pos], c1) ||
-				   !hex_digit_decode(value[pos+1], c2))
-				goto fail;
-			++pos;
+	if (value[0] != '#')
+		return false;
+
+	size_t len = value.size();
+	bool short_form;
+
+	if (len == 9 || len == 7) // #RRGGBBAA or #RRGGBB
+		short_form = false;
+	else if (len == 5 || len == 4) // #RGBA or #RGB
+		short_form = true;
+	else
+		return false;
+
+	bool success = true;
+
+	for (size_t pos = 1, cc = 0; pos < len; pos++, cc++) {
+		assert(cc < sizeof components / sizeof components[0]);
+		if (short_form) {
+			unsigned char d;
+			if (!hex_digit_decode(value[pos], d)) {
+				success = false;
+				break;
+			}
+			components[cc] = (d & 0xf) << 4 | (d & 0xf);
 		} else {
-			// One digit, e.g. hexpattern == "#RGB"
-			if (!hex_digit_decode(value[pos], c1))
-				goto fail;
-			c2 = c1;
+			unsigned char d1, d2;
+			if (!hex_digit_decode(value[pos], d1) ||
+					!hex_digit_decode(value[pos+1], d2)) {
+				success = false;
+				break;
+			}
+			components[cc] = (d1 & 0xf) << 4 | (d2 & 0xf);
+			pos++;	// skip the second digit -- it's already used
 		}
-		u32 colorpart = ((c1 & 0x0f) << 4) | (c2 & 0x0f);
-
-		// Update outcolor with newly read color part
-		if (hexpattern[pos] == 'R')
-			outcolor.setRed(colorpart);
-		else if (hexpattern[pos] == 'G')
-			outcolor.setGreen(colorpart);
-		else if (hexpattern[pos] == 'B')
-			outcolor.setBlue(colorpart);
-		else if (hexpattern[pos] == 'A')
-			outcolor.setAlpha(colorpart);
 	}
 
-	color = outcolor;
-	return true;
+	if (success) {
+		color.setRed(components[0]);
+		color.setGreen(components[1]);
+		color.setBlue(components[2]);
+		color.setAlpha(components[3]);
+	}
 
-fail:
-	if (!quiet)
-		errorstream << "Invalid color: \"" << value << "\"" << std::endl;
-	return false;
+	return success;
+}
+
+struct ColorContainer {
+	ColorContainer();
+	std::map<const std::string, u32> colors;
+};
+
+ColorContainer::ColorContainer()
+{
+	colors["aliceblue"]              = 0xf0f8ff;
+	colors["antiquewhite"]           = 0xfaebd7;
+	colors["aqua"]                   = 0x00ffff;
+	colors["aquamarine"]             = 0x7fffd4;
+	colors["azure"]                  = 0xf0ffff;
+	colors["beige"]                  = 0xf5f5dc;
+	colors["bisque"]                 = 0xffe4c4;
+	colors["black"]                  = 00000000;
+	colors["blanchedalmond"]         = 0xffebcd;
+	colors["blue"]                   = 0x0000ff;
+	colors["blueviolet"]             = 0x8a2be2;
+	colors["brown"]                  = 0xa52a2a;
+	colors["burlywood"]              = 0xdeb887;
+	colors["cadetblue"]              = 0x5f9ea0;
+	colors["chartreuse"]             = 0x7fff00;
+	colors["chocolate"]              = 0xd2691e;
+	colors["coral"]                  = 0xff7f50;
+	colors["cornflowerblue"]         = 0x6495ed;
+	colors["cornsilk"]               = 0xfff8dc;
+	colors["crimson"]                = 0xdc143c;
+	colors["cyan"]                   = 0x00ffff;
+	colors["darkblue"]               = 0x00008b;
+	colors["darkcyan"]               = 0x008b8b;
+	colors["darkgoldenrod"]          = 0xb8860b;
+	colors["darkgray"]               = 0xa9a9a9;
+	colors["darkgreen"]              = 0x006400;
+	colors["darkkhaki"]              = 0xbdb76b;
+	colors["darkmagenta"]            = 0x8b008b;
+	colors["darkolivegreen"]         = 0x556b2f;
+	colors["darkorange"]             = 0xff8c00;
+	colors["darkorchid"]             = 0x9932cc;
+	colors["darkred"]                = 0x8b0000;
+	colors["darksalmon"]             = 0xe9967a;
+	colors["darkseagreen"]           = 0x8fbc8f;
+	colors["darkslateblue"]          = 0x483d8b;
+	colors["darkslategray"]          = 0x2f4f4f;
+	colors["darkturquoise"]          = 0x00ced1;
+	colors["darkviolet"]             = 0x9400d3;
+	colors["deeppink"]               = 0xff1493;
+	colors["deepskyblue"]            = 0x00bfff;
+	colors["dimgray"]                = 0x696969;
+	colors["dodgerblue"]             = 0x1e90ff;
+	colors["firebrick"]              = 0xb22222;
+	colors["floralwhite"]            = 0xfffaf0;
+	colors["forestgreen"]            = 0x228b22;
+	colors["fuchsia"]                = 0xff00ff;
+	colors["gainsboro"]              = 0xdcdcdc;
+	colors["ghostwhite"]             = 0xf8f8ff;
+	colors["gold"]                   = 0xffd700;
+	colors["goldenrod"]              = 0xdaa520;
+	colors["gray"]                   = 0x808080;
+	colors["green"]                  = 0x008000;
+	colors["greenyellow"]            = 0xadff2f;
+	colors["honeydew"]               = 0xf0fff0;
+	colors["hotpink"]                = 0xff69b4;
+	colors["indianred "]             = 0xcd5c5c;
+	colors["indigo "]                = 0x4b0082;
+	colors["ivory"]                  = 0xfffff0;
+	colors["khaki"]                  = 0xf0e68c;
+	colors["lavender"]               = 0xe6e6fa;
+	colors["lavenderblush"]          = 0xfff0f5;
+	colors["lawngreen"]              = 0x7cfc00;
+	colors["lemonchiffon"]           = 0xfffacd;
+	colors["lightblue"]              = 0xadd8e6;
+	colors["lightcoral"]             = 0xf08080;
+	colors["lightcyan"]              = 0xe0ffff;
+	colors["lightgoldenrodyellow"]   = 0xfafad2;
+	colors["lightgray"]              = 0xd3d3d3;
+	colors["lightgreen"]             = 0x90ee90;
+	colors["lightpink"]              = 0xffb6c1;
+	colors["lightsalmon"]            = 0xffa07a;
+	colors["lightseagreen"]          = 0x20b2aa;
+	colors["lightskyblue"]           = 0x87cefa;
+	colors["lightslategray"]         = 0x778899;
+	colors["lightsteelblue"]         = 0xb0c4de;
+	colors["lightyellow"]            = 0xffffe0;
+	colors["lime"]                   = 0x00ff00;
+	colors["limegreen"]              = 0x32cd32;
+	colors["linen"]                  = 0xfaf0e6;
+	colors["magenta"]                = 0xff00ff;
+	colors["maroon"]                 = 0x800000;
+	colors["mediumaquamarine"]       = 0x66cdaa;
+	colors["mediumblue"]             = 0x0000cd;
+	colors["mediumorchid"]           = 0xba55d3;
+	colors["mediumpurple"]           = 0x9370db;
+	colors["mediumseagreen"]         = 0x3cb371;
+	colors["mediumslateblue"]        = 0x7b68ee;
+	colors["mediumspringgreen"]      = 0x00fa9a;
+	colors["mediumturquoise"]        = 0x48d1cc;
+	colors["mediumvioletred"]        = 0xc71585;
+	colors["midnightblue"]           = 0x191970;
+	colors["mintcream"]              = 0xf5fffa;
+	colors["mistyrose"]              = 0xffe4e1;
+	colors["moccasin"]               = 0xffe4b5;
+	colors["navajowhite"]            = 0xffdead;
+	colors["navy"]                   = 0x000080;
+	colors["oldlace"]                = 0xfdf5e6;
+	colors["olive"]                  = 0x808000;
+	colors["olivedrab"]              = 0x6b8e23;
+	colors["orange"]                 = 0xffa500;
+	colors["orangered"]              = 0xff4500;
+	colors["orchid"]                 = 0xda70d6;
+	colors["palegoldenrod"]          = 0xeee8aa;
+	colors["palegreen"]              = 0x98fb98;
+	colors["paleturquoise"]          = 0xafeeee;
+	colors["palevioletred"]          = 0xdb7093;
+	colors["papayawhip"]             = 0xffefd5;
+	colors["peachpuff"]              = 0xffdab9;
+	colors["peru"]                   = 0xcd853f;
+	colors["pink"]                   = 0xffc0cb;
+	colors["plum"]                   = 0xdda0dd;
+	colors["powderblue"]             = 0xb0e0e6;
+	colors["purple"]                 = 0x800080;
+	colors["red"]                    = 0xff0000;
+	colors["rosybrown"]              = 0xbc8f8f;
+	colors["royalblue"]              = 0x4169e1;
+	colors["saddlebrown"]            = 0x8b4513;
+	colors["salmon"]                 = 0xfa8072;
+	colors["sandybrown"]             = 0xf4a460;
+	colors["seagreen"]               = 0x2e8b57;
+	colors["seashell"]               = 0xfff5ee;
+	colors["sienna"]                 = 0xa0522d;
+	colors["silver"]                 = 0xc0c0c0;
+	colors["skyblue"]                = 0x87ceeb;
+	colors["slateblue"]              = 0x6a5acd;
+	colors["slategray"]              = 0x708090;
+	colors["snow"]                   = 0xfffafa;
+	colors["springgreen"]            = 0x00ff7f;
+	colors["steelblue"]              = 0x4682b4;
+	colors["tan"]                    = 0xd2b48c;
+	colors["teal"]                   = 0x008080;
+	colors["thistle"]                = 0xd8bfd8;
+	colors["tomato"]                 = 0xff6347;
+	colors["turquoise"]              = 0x40e0d0;
+	colors["violet"]                 = 0xee82ee;
+	colors["wheat"]                  = 0xf5deb3;
+	colors["white"]                  = 0xffffff;
+	colors["whitesmoke"]             = 0xf5f5f5;
+	colors["yellow"]                 = 0xffff00;
+	colors["yellowgreen"]            = 0x9acd32;
+
+}
+
+static const ColorContainer named_colors;
+
+static bool parseNamedColorString(const std::string &value, video::SColor &color)
+{
+	std::string color_name;
+	std::string alpha_string;
+
+	/* If the string has a # in it, assume this is the start of a specified
+	 * alpha value (if it isn't the string is invalid and the error will be
+	 * caught later on, either because the color name won't be found or the
+	 * alpha value will fail conversion)
+	 */
+	size_t alpha_pos = value.find('#');
+	if (alpha_pos != std::string::npos) {
+		color_name = value.substr(0, alpha_pos);
+		alpha_string = value.substr(alpha_pos + 1);
+	} else {
+		color_name = value;
+	}
+
+	color_name = lowercase(value);
+
+	std::map<const std::string, unsigned>::const_iterator it;
+	it = named_colors.colors.find(color_name);
+	if (it == named_colors.colors.end())
+		return false;
+
+	u32 color_temp = it->second;
+
+	/* An empty string for alpha is ok (none of the color table entries
+	 * have an alpha value either). Color strings without an alpha specified
+	 * are interpreted as fully opaque
+	 *
+	 * For named colors the supplied alpha string (representing a hex value)
+	 * must be exactly two digits. For example:  colorname#08
+	 */
+	if (!alpha_string.empty()) {
+		if (alpha_string.length() != 2)
+			return false;
+
+		unsigned char d1, d2;
+		if (!hex_digit_decode(alpha_string.at(0), d1)
+				|| !hex_digit_decode(alpha_string.at(1), d2))
+			return false;
+		color_temp |= ((d1 & 0xf) << 4 | (d2 & 0xf)) << 24;
+	} else {
+		color_temp |= 0xff << 24;  // Fully opaque
+	}
+
+	color = video::SColor(color_temp);
+
+	return true;
 }
diff --git a/src/util/string.h b/src/util/string.h
index e46fbf4e9b2a48e3682eee3a317927290079cc13..9f7b6673dd0869b1ce6c624aff41cb460f99826b 100644
--- a/src/util/string.h
+++ b/src/util/string.h
@@ -126,13 +126,8 @@ inline std::vector<std::string> str_split(const std::string &str, char delimiter
 inline std::string lowercase(const std::string &s)
 {
 	std::string s2;
-	for(size_t i=0; i<s.size(); i++)
-	{
-		char c = s[i];
-		if(c >= 'A' && c <= 'Z')
-			c -= 'A' - 'a';
-		s2 += c;
-	}
+	for(size_t i = 0; i < s.size(); i++)
+		s2[i] = tolower(s.at(i));
 	return s2;
 }