diff --git a/examples/Animated/Flock.html b/examples/Animated/Flock.html
index 63c1cc17..1c217eea 100644
--- a/examples/Animated/Flock.html
+++ b/examples/Animated/Flock.html
@@ -287,7 +287,7 @@
 		
 		var layer = document.activeLayer;
 		function onKeyDown(event) {
-			if (event.keyCode == 'space')
+			if (event.key == 'space')
 				layer.selected = !layer.selected;
 		}
 	</script>
diff --git a/src/item/Item.js b/src/item/Item.js
index 9029eee6..7ca7feaf 100644
--- a/src/item/Item.js
+++ b/src/item/Item.js
@@ -937,7 +937,7 @@ var Item = this.Item = Base.extend({
 		if (!func || !func._installed) {
 			var hash = {};
 			hash[handler] = function(event) {
-				// Always clear the drag set on mouse-up
+				// Always clear the drag set on mouseup
 				if (name === 'up')
 					sets.drag = {};
 				removeAll(sets[name]);
diff --git a/src/paper.js b/src/paper.js
index 18332d96..182d8029 100644
--- a/src/paper.js
+++ b/src/paper.js
@@ -59,11 +59,7 @@ Base.inject({
 		var start = start || 0,
 			length = length || list.length - start;
 		var obj = list[start];
-		// As a convention, do not return objects that are owned, e.g.
-		// LinkedPoint or SegmentPoint, although they are instances of Point,
-		// since they override properties with beans. Convert these to pure
-		// Points instead, further down.
-		if (obj && !obj._owner && obj instanceof this
+		if (obj instanceof this
 				// If the class defines _readNull, return null when nothing
 				// was provided
 				|| this.prototype._readNull && obj == null && length <= 1)
@@ -122,8 +118,28 @@ Base.inject({
 		});
 	},
 
+	camelize: function(str) {
+		return str.replace(/-(\w)/g, function(all, chr) {
+			return chr.toUpperCase();
+		});
+	},
+
+	/**
+	 * Utility function for rendering numbers to strings at a precision of up
+	 * to 5 fractional digits.
+	 */
 	formatNumber: function(num) {
 		return (Math.round(num * 100000) / 100000).toString();
+	},
+
+	/**
+	 * Utility function for rendering objects to strings, in object literal
+	 * notation.
+	 */
+	formatObject: function(obj) {
+		return '{ ' + Base.each(obj, function(value, key) {
+			this.push(key + ': ' + value);
+		}, []).join(', ') + ' }';
 	}
 });
 
diff --git a/src/tool/Tool.js b/src/tool/Tool.js
index 8d789df0..0717fccd 100644
--- a/src/tool/Tool.js
+++ b/src/tool/Tool.js
@@ -31,7 +31,7 @@ var Tool = this.Tool = ToolHandler.extend(new function() {
 			this.events = {
 				mousedown: function(event) {
 					curPoint = viewToArtwork(event, that._document);
-					that.onHandleEvent('mouse-down', curPoint, event);
+					that.onHandleEvent('mousedown', curPoint, event);
 					if (that.onMouseDown)
 						that._document.redraw();
 					if (that.eventInterval != null) {
@@ -54,9 +54,9 @@ var Tool = this.Tool = ToolHandler.extend(new function() {
 					if (dragging && !onlyMove) {
 						curPoint = point || curPoint;
 						if (curPoint)
-							that.onHandleEvent('mouse-drag', curPoint, event);
+							that.onHandleEvent('mousedrag', curPoint, event);
 					} else if (!dragging || onlyMove) {
-						that.onHandleEvent('mouse-move', point, event);
+						that.onHandleEvent('mousemove', point, event);
 					}
 					if (that.onMouseMove || that.onMouseDrag)
 						that._document.redraw();
@@ -67,7 +67,7 @@ var Tool = this.Tool = ToolHandler.extend(new function() {
 						curPoint = null;
 						if (that.eventInterval != null)
 							clearInterval(this.timer);
-						that.onHandleEvent('mouse-up',
+						that.onHandleEvent('mouseup',
 								viewToArtwork(event, that._document), event);
 						if (that.onMouseUp)
 							that._document.redraw();
diff --git a/src/tool/ToolEvent.js b/src/tool/ToolEvent.js
index 5a0df5d1..b68b1fec 100644
--- a/src/tool/ToolEvent.js
+++ b/src/tool/ToolEvent.js
@@ -39,25 +39,13 @@ var ToolEvent = this.ToolEvent = Base.extend({
 		this.type = type;
 		this.event = event;
 	},
-	
-	toString: function() {
-		return '{ type: ' + this.type 
-				+ ', point: ' + this.point
-				+ ', count: ' + this.count
-				+ ', modifiers: ' + this.modifiers
-				+ ' }';
-	},
 
 	/**
 	 * Convenience method to allow local overrides of point values.
 	 * See application below.
 	 */
-	choosePoint: function(point, toolPoint) {
-		if (point)
-			return point;
-		if (toolPoint)
-			return new Point(toolPoint);
-		return null;
+	_choosePoint: function(point, toolPoint) {
+		return point ? point : toolPoint ? toolPoint.clone() : null;
 	},
 
 	/**
@@ -78,7 +66,7 @@ var ToolEvent = this.ToolEvent = Base.extend({
 	 * </code>
 	 */
 	getPoint: function() {
-		return this.choosePoint(this._point, this.tool.point);
+		return this._choosePoint(this._point, this.tool.point);
 	},
 
 	setPoint: function(point) {
@@ -90,7 +78,7 @@ var ToolEvent = this.ToolEvent = Base.extend({
 	 * event was fired.
 	 */
 	getLastPoint: function() {
-		return this.choosePoint(this._lastPoint, this.tool.lastPoint);
+		return this._choosePoint(this._lastPoint, this.tool.lastPoint);
 	},
 
 	setLastPoint: function(lastPoint) {
@@ -102,7 +90,7 @@ var ToolEvent = this.ToolEvent = Base.extend({
 	 * was last clicked.
 	 */
 	getDownPoint: function() {
-		return this.choosePoint(this._downPoint, this.tool.downPoint);
+		return this._choosePoint(this._downPoint, this.tool.downPoint);
 	},
 
 	setDownPoint: function(downPoint) {
@@ -117,7 +105,7 @@ var ToolEvent = this.ToolEvent = Base.extend({
 	 */
 	getMiddlePoint: function() {
 		// For explanations, see getDelta()
-		if (this._middlePoint == null && this.tool.lastPoint != null) {
+		if (!this._middlePoint && this.tool.lastPoint) {
 			// (point + lastPoint) / 2
 			return this.tool.point.add(this.tool.lastPoint).divide(2);
 		}
@@ -130,8 +118,8 @@ var ToolEvent = this.ToolEvent = Base.extend({
 
 	/**
 	 * The difference between the current position and the last position of the
-	 * mouse when the event was fired. In case of the mouse-up event, the
-	 * difference to the mouse-down position is returned.
+	 * mouse when the event was fired. In case of the mouseup event, the
+	 * difference to the mousedown position is returned.
 	 */
 	getDelta: function() {
 		// Do not put the calculated delta into delta, since this only reserved
@@ -139,10 +127,9 @@ var ToolEvent = this.ToolEvent = Base.extend({
 		// Instead, keep calculating the delta each time, so the result can be
 		// directly modified by the script without changing the internal values.
 		// We could cache this and use clone, but this is almost as fast...
-		if (this._delta == null && this.tool.lastPoint != null) {
-			return this.tool.point.subtract(this.tool.lastPoint);
-		}
-		return this._delta;
+		return this._delta && this.tool.lastPoint
+		 		? this.tool.point.subtract(this.tool.lastPoint)
+				: this._delta;
 	},
 
 	setDelta: function(delta) {
@@ -168,32 +155,21 @@ var ToolEvent = this.ToolEvent = Base.extend({
 	 * </code>
 	 */
 	getCount: function() {
-		switch (this.type) {
-		case 'mouse-down':
-		case 'mouse-up':
-			// Return downCount for both mouse down and up, since
-			// the count is the same.
-			return this.tool.downCount;
-		default:
-			return this.tool.count;
-		}
+		// Return downCount for both mouse down and up, since
+		// the count is the same.
+		return /^mouse(down|up)$/.test(this.type)
+			? this.tool.downCount
+			: this.tool.count;
 	},
 
 	setCount: function(count) {
-		switch (this.type) {
-		case 'mouse-down':
-		case 'mouse-up':
-			this.tool.downCount = count;
-			break;
-		default:
-			this.tool.count = count;
-			break;
-		}
+		this.tool[/^mouse(down|up)$/.test(this.type) ? 'downCount' : 'count']
+			= count;
 	},
 	
 	getModifiers: function() {
 		return Key.modifiers;
-	}
+	},
 
 	// TODO: implement hitTest first
 	// getItem: function() {
@@ -215,4 +191,12 @@ var ToolEvent = this.ToolEvent = Base.extend({
 	// setItem: function(Item item) {
 	// 	this.item = item;
 	// }
+	
+	toString: function() {
+		return '{ type: ' + this.type 
+				+ ', point: ' + this.getPoint()
+				+ ', count: ' + this.getCount()
+				+ ', modifiers: ' + this.getModifiers()
+				+ ' }';
+	}
 });
diff --git a/src/tool/ToolHandler.js b/src/tool/ToolHandler.js
index 87faec5f..8c164974 100644
--- a/src/tool/ToolHandler.js
+++ b/src/tool/ToolHandler.js
@@ -35,8 +35,8 @@ var ToolHandler = this.ToolHandler = Base.extend({
 	 * 
 	 * Sample code:
 	 * <code>
-	 * // Fire the onMouseDrag event after the user has dragged
-	 * // more then 5 points from the last onMouseDrag event:
+	 * // Fire the onMouseDrag event after the user has dragged more then 5
+	 * // points from the last onMouseDrag event:
 	 * tool.minDistance = 5;
 	 * </code>
 	 */
@@ -98,42 +98,38 @@ var ToolHandler = this.ToolHandler = Base.extend({
 		this.lastPoint = this.point;
 		this.point = pt;
 		switch (type) {
-		case 'mouse-down':
+		case 'mousedown':
 			this.lastPoint = this.downPoint;
 			this.downPoint = this.point;
 			this.downCount++;
 			break;
-		case 'mouse-up':
-			// Mouse up events return the down point for last point,
-			// so delta is spanning over the whole drag.
+		case 'mouseup':
+			// Mouse up events return the down point for last point, so delta is
+			// spanning over the whole drag.
 			this.lastPoint = this.downPoint;
 			break;
 		}
-		if (start) {
-			this.count = 0;
-		} else {
-			this.count++;
-		}
+		this.count = start ? 0 : this.count + 1;
 		return true;
 	},
 
 	onHandleEvent: function(type, pt, event) {
 		switch (type) {
-		case 'mouse-down':
+		case 'mousedown':
 			this.updateEvent(type, pt, null, null, true, false, false);
 			if (this.onMouseDown)
 				this.onMouseDown(new ToolEvent(this, type, event));
 			break;
-		case 'mouse-drag':
-			// In order for idleInterval drag events to work, we need to
-			// not check the first call for a change of position. 
-			// Subsequent calls required by min/maxDistance functionality
-			// will require it, otherwise this might loop endlessly.
+		case 'mousedrag':
+			// In order for idleInterval drag events to work, we need to not
+			// check the first call for a change of position. Subsequent calls
+			// required by min/maxDistance functionality will require it,
+			// otherwise this might loop endlessly.
 			var needsChange = false,
-			// If the mouse is moving faster than maxDistance, do not
-			// produce events for what is left after the first event is
-			// generated in case it is shorter than maxDistance, as this
-			// would produce weird results. matchMaxDistance controls this.
+			// If the mouse is moving faster than maxDistance, do not produce
+			// events for what is left after the first event is generated in
+			// case it is shorter than maxDistance, as this would produce weird
+			// results. matchMaxDistance controls this.
 				matchMaxDistance = false;
 			while (this.updateEvent(type, pt, this.minDistance,
 					this.maxDistance, false, needsChange, matchMaxDistance)) {
@@ -143,11 +139,11 @@ var ToolHandler = this.ToolHandler = Base.extend({
 				matchMaxDistance = true;
 			}
 			break;
-		case 'mouse-up':
-			// If the last mouse drag happened in a different place, call
-			// mouse drag first, then mouse up.
+		case 'mouseup':
+			// If the last mouse drag happened in a different place, call mouse
+			// drag first, then mouse up.
 			if ((this.point.x != pt.x || this.point.y != pt.y)
-					&& this.updateEvent('mouse-drag', pt, this.minDistance,
+					&& this.updateEvent('mousedrag', pt, this.minDistance,
 							this.maxDistance, false, false, false)) {
 				if (this.onMouseDrag)
 					this.onMouseDrag(new ToolEvent(this, type, event));
@@ -156,11 +152,11 @@ var ToolHandler = this.ToolHandler = Base.extend({
 					false, false);
 			if (this.onMouseUp)
 				this.onMouseUp(new ToolEvent(this, type, event));
-			// Start with new values for 'mouse-move'
+			// Start with new values for 'mousemove'
 			this.updateEvent(type, pt, null, null, true, false, false);
 			this.firstMove = true;
 			break;
-		case 'mouse-move':
+		case 'mousemove':
 			while (this.updateEvent(type, pt, this.minDistance,
 					this.maxDistance, this.firstMove, true, false)) {
 				if (this.onMouseMove)
diff --git a/src/ui/Event.js b/src/ui/Event.js
index 8a7d0919..f05b6242 100644
--- a/src/ui/Event.js
+++ b/src/ui/Event.js
@@ -15,6 +15,8 @@
  */
 
 var Event = this.Event = Base.extend({
+	beans: true,
+
 	initialize: function(event) {
 		this.event = event;
 	},
diff --git a/src/ui/Key.js b/src/ui/Key.js
index 1342a8d7..6e2b2a08 100644
--- a/src/ui/Key.js
+++ b/src/ui/Key.js
@@ -16,18 +16,21 @@
 
 var Key = this.Key = new function() {
 	// TODO: make sure the keys are called the same as in Scriptographer
-	// Missing: tab, cancel, clear, pause, page-down, page-up, end, home, comma,
-	// minus, period, slash, etc etc etc.
+	// Missing: tab, cancel, clear, page-down, page-up, comma, minus, period,
+	// slash, etc etc etc.
 
 	var keys = {
 		 8: 'backspace',
 		13: 'enter',
 		16: 'shift',
 		17: 'control',
-		19: 'option', // was alt
-		20: 'capsLock',
+		18: 'option',
+		19: 'pause',
+		20: 'caps-lock',
 		27: 'escape',
 		32: 'space',
+		35: 'end',
+		36: 'home',
 		37: 'left',
 		38: 'up',
 		39: 'right',
@@ -41,57 +44,84 @@ var Key = this.Key = new function() {
 		control: false,
 		option: false,
 		command: false,
-		capsLock: false
+		capsLock: false,
+
+		toString: function() {
+			return Base.formatObject(this);
+		}
 	},
 
-	keyCodes = {},
-	downCode,
-	downTimer;
+	// Since only keypress gets proper keyCodes that are actually representing
+	// characters, we need to perform a little trickery here to use these codes
+	// in onKeyDown/Up: keydown is used to store the downCode and handle
+	// modifiers and special keys such as arrows, space, etc, keypress fires the
+	// actual onKeyDown event and maps the keydown keyCode to the keypress 
+	// charCode so keyup can do the right thing too.
+	charCodeMap = {}, // keyCode -> charCode mappings for pressed keys
+	keyMap = {}, // Map for currently pressed keys
+	downCode; // The last keyCode from keydown
 
-	function handleKey(down, code, event) {
-		var character = String.fromCharCode(code),
-			keyCode = keys[code] || character.toLowerCase(),
+	function handleKey(down, keyCode, charCode, event) {
+		var character = String.fromCharCode(charCode),
+			key = keys[keyCode] || character.toLowerCase(),
 			handler = down ? 'onKeyDown' : 'onKeyUp';
-		console.log(handler, keyCode, character);
-		if (modifiers[keyCode] !== undefined) {
-			modifiers[keyCode] = down;
-		} else if (paper.tool && paper.tool[handler]) {
+		keyMap[key] = down;
+		if (paper.tool && paper.tool[handler]) {
 			// Call the onKeyDown or onKeyUp handler if present
 			// When the handler function returns false, prevent the
 			// default behaviour of the key event:
 			// PORT: Add to Sg
-			var keyEvent = new KeyEvent(down, keyCode, character, event);
+			var keyEvent = new KeyEvent(down, key, character, event);
 			if (paper.tool[handler](keyEvent) === false) {
 				keyEvent.preventDefault();
 			}
 		}
 	}
 
-	// Since only keypress gest proper keyCodes that are actually representing
-	// characters, we need to add a little timeout to keydown events to see if
-	// they are follow immediately by a keypress, and if so, map the keyCode
-	// from the keydown to the one from keypress, so keyup still knows what
-	// code has now been released.
 	DomEvent.add(document, {
 		keydown: function(event) {
-			var code = downCode = event.which || event.keyCode;
-			downTimer = setTimeout(function() {
-				keyCodes[code] = code;
-				handleKey(true, code, event);
-			}, 1);
+			var code = event.which || event.keyCode;
+			// If the keyCode is in keys, it needs to be handled by keydown and
+			// not in keypress after (arrows for example wont be triggering
+			// a keypress, but space would).
+			var key = keys[code], name;
+			if (key) {
+				// Do not fire handleKey for modifiers, but for other keys such
+				// ass arrows, delete, backspace, etc.
+				if (modifiers[name = Base.camelize(key)] !== undefined) {
+					modifiers[name] = true;
+				} else {
+					// No char code for special keys, but mark as pressed
+					charCodeMap[code] = 0;
+					handleKey(true, code, null, event);
+				}
+				// Do not set downCode as we handled it already. Space would
+				// be handled twice otherwise, once here, once in keypress.
+			} else {
+				downCode = code;
+			}
 		},
 
 		keypress: function(event) {
-			clearTimeout(downTimer);
-			var code = event.which || event.keyCode;
-			keyCodes[downCode] = code;
-			handleKey(true, code, event);
+			if (downCode != null) {
+				var code = event.which || event.keyCode;
+				// Link the downCode from keydown with the code form keypress, so
+				// keyup can retrieve that code again.
+				charCodeMap[downCode] = code;
+				handleKey(true, downCode, code, event);
+				downCode = null;
+			}
 		},
 
 		keyup: function(event) {
-			var code = event.which || event.keyCode;
-			handleKey(false, keyCodes[code], event);
-			delete keyCodes[code];
+			var code = event.which || event.keyCode,
+				key = keys[code], name;
+			if (key && modifiers[name = Base.camelize(key)] !== undefined) {
+				modifiers[name] = false
+			} else if (charCodeMap[code] != null) {
+				handleKey(false, code, charCodeMap[code], event);
+				delete charCodeMap[code];
+			}
 		}
 	});
 
@@ -99,7 +129,7 @@ var Key = this.Key = new function() {
 		modifiers: modifiers,
 
 		isDown: function(key) {
-			return !!activeKeys[key];
+			return !!keyMap[key];
 		}
 	};
 };
\ No newline at end of file
diff --git a/src/ui/KeyEvent.js b/src/ui/KeyEvent.js
index 3ea14f7c..d63eb315 100644
--- a/src/ui/KeyEvent.js
+++ b/src/ui/KeyEvent.js
@@ -16,11 +16,19 @@
 
 var KeyEvent = this.KeyEvent = Event.extend(new function() {
 	return {
-		initialize: function(down, keyCode, character, event) {
+		initialize: function(down, key, character, event) {
 			this.base(event);
-			this.type = down ? 'key-down' : 'key-up';
-			this.keyCode = keyCode;
+			this.type = down ? 'keydown' : 'keyup';
+			this.key = key;
 			this.character = character;
+		},
+	
+		toString: function() {
+			return '{ type: ' + this.type 
+					+ ', key: ' + this.key
+					+ ', character: ' + this.character
+					+ ', modifiers: ' + this.getModifiers()
+					+ ' }';
 		}
 	};
 });
diff --git a/src/util/PaperScript.js b/src/util/PaperScript.js
index 4c7aefdc..121d0bd5 100644
--- a/src/util/PaperScript.js
+++ b/src/util/PaperScript.js
@@ -134,14 +134,13 @@ var PaperScript = this.PaperScript = new function() {
 			var doc = paper.document,
 				tool = paper.tool = /on(?:Key|Mouse)(?:Up|Down|Move|Drag)/.test(code)
 					&& new Tool(null, doc),
-				onEditOptions, onOptions, onSelect, onDeselect, onReselect,
-				onMouseDown, onMouseUp, onMouseDrag, onMouseMove, onKeyDown,
-				onKeyUp,
+				onEditOptions, onSelect, onDeselect, onReselect, onMouseDown,
+				onMouseUp, onMouseDrag, onMouseMove, onKeyDown, onKeyUp,
 				res = eval(compile(code));
 			if (tool) {
-				Base.each(['onEditOptions', 'onOptions', 'onSelect',
-						'onDeselect', 'onReselect', 'onMouseDown', 'onMouseUp',
-						'onMouseDrag', 'onMouseMove', 'onKeyDown', 'onKeyUp'],
+				Base.each(['onEditOptions', 'onSelect', 'onDeselect',
+						'onReselect', 'onMouseDown', 'onMouseUp', 'onMouseDrag',
+						'onMouseMove', 'onKeyDown', 'onKeyUp'],
 					function(key) {
 						tool[key] = eval(key);
 					}