diff --git a/core/modules/parsers/wikiparser/rules/image.js b/core/modules/parsers/wikiparser/rules/image.js
new file mode 100644
index 000000000..11657ca48
--- /dev/null
+++ b/core/modules/parsers/wikiparser/rules/image.js
@@ -0,0 +1,137 @@
+/*\
+title: $:/core/modules/parsers/wikiparser/rules/image.js
+type: application/javascript
+module-type: wikirule
+
+Wiki text inline rule for embedding images. For example:
+
+```
+[img[http://tiddlywiki.com/fractalveg.jpg]]
+[img width=23 height=24 [http://tiddlywiki.com/fractalveg.jpg]]
+[img 23x24 [http://tiddlywiki.com/fractalveg.jpg]]
+[img width={{!!width}} height={{!!height}} [http://tiddlywiki.com/fractalveg.jpg]]
+[img {{!!width}}x{{!!height}} [http://tiddlywiki.com/fractalveg.jpg]]
+[img[Description of image|http://tiddlywiki.com/fractalveg.jpg]]
+[img[TiddlerTitle]]
+[img[Description of image|TiddlerTitle]]
+```
+
+Generates the `<$image>` widget.
+
+\*/
+(function(){
+
+/*jslint node: true, browser: true */
+/*global $tw: false */
+"use strict";
+
+exports.name = "image";
+exports.types = {inline: true};
+
+exports.init = function(parser) {
+ this.parser = parser;
+};
+
+exports.findNextMatch = function(startPos) {
+ // Find the next tag
+ this.nextImage = this.findNextImage(this.parser.source,startPos);
+ return this.nextImage ? this.nextImage.start : undefined;
+};
+
+exports.parse = function() {
+ // Move past the match
+ this.parser.pos = this.nextImage.end;
+ var node = {
+ type: "element",
+ tag: "$image",
+ attributes: this.nextImage.attributes
+ };
+ return [node];
+};
+
+/*
+Find the next image from the current position
+*/
+exports.findNextImage = function(source,pos) {
+ // A regexp for finding candidate HTML tags
+ var reLookahead = /(\[img)/g;
+ // Find the next candidate
+ reLookahead.lastIndex = pos;
+ var match = reLookahead.exec(source);
+ while(match) {
+ // Try to parse the candidate as a tag
+ var tag = this.parseImage(source,match.index);
+ // Return success
+ if(tag) {
+ return tag;
+ }
+ // Look for the next match
+ reLookahead.lastIndex = match.index + 1;
+ match = reLookahead.exec(source);
+ }
+ // Failed
+ return null;
+};
+
+/*
+Look for an image at the specified position. Returns null if not found, otherwise returns {type: "element", name: "$image", attributes: [], isSelfClosing:, start:, end:,}
+*/
+exports.parseImage = function(source,pos) {
+ var token,
+ node = {
+ type: "element",
+ name: "$image",
+ start: pos,
+ attributes: {}
+ };
+ // Skip whitespace
+ pos = $tw.utils.skipWhiteSpace(source,pos);
+ // Look for the `[img`
+ token = $tw.utils.parseTokenString(source,pos,"[img");
+ if(!token) {
+ return null;
+ }
+ pos = token.end;
+ // Skip whitespace
+ pos = $tw.utils.skipWhiteSpace(source,pos);
+ // Process attributes
+ if(source.charAt(pos) !== "[") {
+ var attribute = $tw.utils.parseAttribute(source,pos);
+ while(attribute) {
+ node.attributes[attribute.name] = attribute;
+ pos = attribute.end;
+ pos = $tw.utils.skipWhiteSpace(source,pos);
+ if(source.charAt(pos) !== "[") {
+ // Get the next attribute
+ attribute = $tw.utils.parseAttribute(source,pos);
+ } else {
+ attribute = null;
+ }
+ }
+ }
+ // Skip whitespace
+ pos = $tw.utils.skipWhiteSpace(source,pos);
+ // Look for the `[` after the attributes
+ token = $tw.utils.parseTokenString(source,pos,"[");
+ if(!token) {
+ return null;
+ }
+ pos = token.end;
+ // Skip whitespace
+ pos = $tw.utils.skipWhiteSpace(source,pos);
+ // Get the source up to the terminating `]]`
+ token = $tw.utils.parseTokenRegExp(source,pos,/(?:([^|\]]*?)\|)?([^\]]+?)\]\]/g);
+ if(!token) {
+ return null;
+ }
+ pos = token.end;
+ if(token.match[1]) {
+ node.attributes.tooltip = {type: "string", value: token.match[1].trim()};
+ }
+ node.attributes.source = {type: "string", value: (token.match[2] || "").trim()};
+ // Update the end position
+ node.end = pos;
+ return node;
+};
+
+})();
diff --git a/core/modules/widgets/image.js b/core/modules/widgets/image.js
new file mode 100644
index 000000000..1ab024ef6
--- /dev/null
+++ b/core/modules/widgets/image.js
@@ -0,0 +1,123 @@
+/*\
+title: $:/core/modules/widgets/image.js
+type: application/javascript
+module-type: widget
+
+The image widget displays an image referenced with an external URI or with a local tiddler title.
+
+```
+<$image src="TiddlerTitle" width="320" height="400" class="classnames">
+```
+
+The image source can be the title of an existing tiddler or the URL of an external image.
+
+External images always generate an HTML `` tag.
+
+Tiddlers that have a _canonical_uri field generate an HTML `` tag with the src attribute containing the URI.
+
+Tiddlers that contain image data generate an HTML `` tag with the src attribute containing a base64 representation of the image.
+
+Tiddlers that contain wikitext could be rendered to a DIV of the usual size of a tiddler, and then transformed to the size requested.
+
+The width and height attributes are interpreted as a number of pixels, and do not need to include the "px" suffix.
+
+\*/
+(function(){
+
+/*jslint node: true, browser: true */
+/*global $tw: false */
+"use strict";
+
+var Widget = require("$:/core/modules/widgets/widget.js").widget;
+
+var ImageWidget = function(parseTreeNode,options) {
+ this.initialise(parseTreeNode,options);
+};
+
+/*
+Inherit from the base widget class
+*/
+ImageWidget.prototype = new Widget();
+
+/*
+Render this widget into the DOM
+*/
+ImageWidget.prototype.render = function(parent,nextSibling) {
+ this.parentDomNode = parent;
+ this.computeAttributes();
+ this.execute();
+ // Create element
+ // Determine what type of image it is
+ var tag = "img", src = "",
+ tiddler = this.wiki.getTiddler(this.imageSource);
+ if(!tiddler) {
+ // The source isn't the title of a tiddler, so we'll assume it's a URL
+ src = this.imageSource;
+ } else {
+ // Check if it is an image tiddler
+ if(this.wiki.isImageTiddler(this.imageSource)) {
+ // Render the appropriate element for the image type
+ var type = tiddler.fields.type,
+ text = tiddler.fields.text;
+ switch(type) {
+ case "application/pdf":
+ tag = "embed";
+ src = "data:application/pdf;base64," + text;
+ break;
+ case "image/svg+xml":
+ src = "data:image/svg+xml," + encodeURIComponent(text);
+ break;
+ default:
+ src = "data:" + type + ";base64," + text;
+ break;
+ }
+ }
+ }
+ // Create the element and assign the attributes
+ var domNode = this.document.createElement(tag);
+ domNode.setAttribute("src",src);
+ if(this.imageClass) {
+ domNode.setAttribute("class",this.imageClass);
+ }
+ if(this.imageWidth) {
+ domNode.setAttribute("width",parseInt(this.imageWidth,10) + "px");
+ }
+ if(this.imageHeight) {
+ domNode.setAttribute("height",parseInt(this.imageHeight,10) + "px");
+ }
+ if(this.imageTooltip) {
+ domNode.setAttribute("title",this.imageTooltip);
+ }
+ // Insert element
+ parent.insertBefore(domNode,nextSibling);
+ this.domNodes.push(domNode);
+};
+
+/*
+Compute the internal state of the widget
+*/
+ImageWidget.prototype.execute = function() {
+ // Get our parameters
+ this.imageSource = this.getAttribute("source");
+ this.imageWidth = this.getAttribute("width");
+ this.imageHeight = this.getAttribute("height");
+ this.imageClass = this.getAttribute("class");
+ this.imageTooltip = this.getAttribute("tooltip");
+};
+
+/*
+Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
+*/
+ImageWidget.prototype.refresh = function(changedTiddlers) {
+ var changedAttributes = this.computeAttributes();
+ if(changedAttributes.source || changedAttributes.width || changedAttributes.height || changedAttributes["class"] || changedAttributes.tooltip || changedTiddlers[this.imageSource]) {
+ this.refreshSelf();
+ return true;
+ } else {
+ return false;
+ }
+};
+
+exports.image = ImageWidget;
+
+})();
diff --git a/editions/tw5.com/tiddlers/widgets/ImageWidget.tid b/editions/tw5.com/tiddlers/widgets/ImageWidget.tid
new file mode 100644
index 000000000..f616ea97d
--- /dev/null
+++ b/editions/tw5.com/tiddlers/widgets/ImageWidget.tid
@@ -0,0 +1,20 @@
+title: ImageWidget
+created: 20140416160234142
+modified: 20140416160234142
+tags: widget
+
+! Introduction
+
+The image widget displays images that can be specified as a remote URL or the title of a local tiddler containing the image.
+
+! Content and Attributes
+
+Any content of the `<$image>` widget is ignored.
+
+|!Attribute |!Description |
+|source |The URL of the image, or the title of an image tiddler |
+|width |The width of the image as a number |
+|height |The height of the image |
+|tooltip |The tooltip to be displayed over the image |
+|class |CSS classes to be assigned to the `` element |
+
diff --git a/editions/tw5.com/tiddlers/wikitext/Images in WikiText.tid b/editions/tw5.com/tiddlers/wikitext/Images in WikiText.tid
index 8d14363bc..dc3556c7e 100644
--- a/editions/tw5.com/tiddlers/wikitext/Images in WikiText.tid
+++ b/editions/tw5.com/tiddlers/wikitext/Images in WikiText.tid
@@ -1,10 +1,44 @@
created: 20131205160221762
-modified: 20131205160234142
+modified: 20140416160234142
tags: wikitext
title: Images in WikiText
type: text/vnd.tiddlywiki
-To display an image stored in a tiddler just transclude that tiddler:
+! Image Formatting
+
+Images can be included in WikiText with the following syntax:
+
+```
+[img[Motovun Jack.jpg]]
+[img[http://tiddlywiki.com/favicon.ico]]
+```
+
+If the image source is the title of an image tiddler then that tiddler is directly displayed. Otherwise it is interpreted as a URL and an HTML `` tag is generated with the `src` attribute containing the URL.
+
+A tooltip can also be specified:
+
+```
+[img[An explanatory tooltip|Motovun Jack.jpg]]
+```
+
+Attributes can be provided to specify CSS classes and the image width and height:
+
+```
+[img width=32 [Motovun Jack.jpg]]
+[img width=32 class="tw-image" [Motovun Jack.jpg]]
+```
+
+Note that attributes can be specified as transclusions or variable references:
+
+```
+[img width={{!!mywidth}} class=<> [Motovun Jack.jpg]]
+```
+
+The image syntax is a shorthand for invoking the ImageWidget.
+
+! Displaying Images via Transclusion
+
+You can also display an image stored in a tiddler by transcluding that tiddler. The disadvantage of this approach is that there is no direct way to control the size of the image.
```
{{Motovun Jack.jpg}}
@@ -13,3 +47,4 @@ To display an image stored in a tiddler just transclude that tiddler:
Renders as:
{{Motovun Jack.jpg}}
+