diff --git a/_includes/header.html b/_includes/header.html
index 7f7e4f7..a8cfcf5 100644
--- a/_includes/header.html
+++ b/_includes/header.html
@@ -11,6 +11,7 @@
Minetest Tutorials
1 - Folder Structure
2 - Nodes, Items and Crafting
+ 3 - Formspecs
Index
diff --git a/book_index.md b/book_index.md
index 6108ff3..9e764b6 100644
--- a/book_index.md
+++ b/book_index.md
@@ -7,10 +7,14 @@ layout: default
## C
* Craft item
* [Registering Nodes and Items]({{ page.root }}chapters/nodes_items_crafting.html#registering-a-craftitem)
+* Context
+ * [Formspecs]({{ page.root }}chapters/formspecs.html)
## F
* Food
* [Registering Nodes and Items]({{ page.root }}chapters/nodes_items_crafting.html#foods)
+* Formspec
+ * [Formspecs]({{ page.root }}chapters/formspecs.html)
## I
* Item String
@@ -21,5 +25,3 @@ layout: default
* [Registering Nodes and Items]({{ page.root }}chapters/nodes_items_crafting.html#item-strings)
* Nodes
* [Registering Nodes and Items]({{ page.root }}chapters/nodes_items_crafting.html#registering-a-basic-node)
-* Node boxes
- * [Registering Nodes and Items]({{ page.root }}chapters/nodes_items_crafting.html#node-boxes)
diff --git a/chapters/formspecs.md b/chapters/formspecs.md
new file mode 100644
index 0000000..e57f283
--- /dev/null
+++ b/chapters/formspecs.md
@@ -0,0 +1,181 @@
+---
+title: Formspecs
+layout: default
+root: ../
+---
+
+Introduction
+------------
+
+In this chapter we will learn how to create a formspec and display it to the user.
+A formspec is the specification code for a form.
+In Minetest, forms are windows like the Inventory which allow you to move your mouse
+and enter information.
+You should consider using Heads Up Display (HUD) elements if you do
+not need to get user input - notifications, for example - as unexpected windows
+tend to disrupt game play.
+
+* Formspec syntax
+* Displaying Forms
+* Callbacks
+* Contexts
+
+
+Formspec Syntax
+---------------
+
+Formspecs have a rather weird syntax.
+They consist of a series of tags which are in the following form:
+
+ element[param1;param2;...]
+
+Firstly the element type is declared, and then the attributes are given in square brackets.
+
+### Size[w, h]
+
+Nearly all forms have a size tag. They are used to declare the size of the window required.
+**Forms don't use pixels as co-ordinates, they use a grid**, based on inventories.
+A size of (1, 1) means the form is big enough to host a 1x1 inventory.
+The reason this is used is because it is independent on screen resolution -
+The form should work just as well on large screens as small screens.
+You can use decimals in sizes and co-ordinates.
+
+ size[5,2]
+
+Co-ordinates and sizes only use one attribute.
+The x and y values are separated by a comma, as you can see above.
+
+### Field[x, y; w, h; name; label; default]
+
+Most elements follow a similar form to this. The name attribute is used in callbacks
+to get the submitted information. The others are pretty self-explaintary.
+
+ field[1,1;3,1;firstname;Firstname;]
+
+It is perfectly valid to not define an attribute, like above.
+
+### Other Elements
+
+You should look in [lua_api.txt](https://github.com/minetest/minetest/blob/master/doc/lua_api.txt#L1019)
+for a list of all possible elements, just search for "Formspec".
+It is near line 1019, at time of writing.
+
+Displaying Formspecs
+--------------------
+
+Here is a generalized way to show a formspec
+
+ minetest.show_formspec(playername, formname, formspec)
+
+Formnames should be itemnames, however that is not enforced.
+There is no need to override a formspec here, formspecs are not registered like
+nodes and items are, instead the formspec code is sent to the player's client for them
+to see, along with the formname.
+Formnames are used in callbacks to identify which form has been submitted,
+and see if the callback is relevant.
+
+Callbacks
+---------
+
+{% highlight lua %}
+-- Show form when the /formspec command is used.
+minetest.register_chatcommand("formspec", {
+ func = function(name, param)
+ minetest.show_formspec(name, "mymod:form",
+ "size[4,3]" ..
+ "label[0,0;Hello, " .. name .. "]" ..
+ "field[1,1.5;3,1;name;Name;]" ..
+ "button_exit[1,2;2,1;exit;Save]")
+ end
+})
+
+-- Register callback
+minetest.register_on_player_receive_fields(function(player, formname, fields)
+ if formname ~= "mymod:form" then
+ -- Formname is not mymod:form,
+ -- exit callback.
+ return false
+ end
+
+ -- Send message to player.
+ minetest.chat_send_player(player:get_player_name(), "You said: " .. fields.name .. "!")
+
+ -- Return true to stop other minetest.register_on_player_receive_fields
+ -- from receiving this submission.
+ return true
+end)
+{% endhighlight %}
+
+The function given in minetest.register_on_player_receive_fields is called
+everytime a user submits a form. Most callbacks will check the formname given
+to the function, and exit if it is not the right form. However, some callbacks
+may need to work on multiple forms, or all forms - it depends on what you
+want to do.
+
+### Fields
+
+The fields parameter to the function is a table, index by string, of the values
+submitted by the user. You can access values in the table by doing fields.name,
+where 'name' is the name of the element.
+
+As well as having the values of each element, you can also get which button
+was clicked. In this case, the button called 'exit' was clicked, so fields.exit
+will be true.
+
+Some elements can submit the form without the user having to click a button,
+such as a check box. You can detect for these cases by looking
+for a clicked button.
+
+Contexts
+--------
+
+In quite a lot of cases you want your minetest.show_formspec to give information
+to the callback which you don't want to have to send to the client. Information
+such as what a chat command was called with, or what the dialog is about.
+
+Let's say you are making a form to handle land protection information. Here is
+how you would do it:
+
+{% highlight lua %}
+local land_formspec_context = {}
+
+minetest.register_chatcommand("land", {
+ func = function(name, param)
+ if param == "" then
+ minetest.chat_send_player(name, "Incorrect parameters - supply a land ID")
+ return
+ end
+
+ -- Save information
+ land_formspec_context[name] = {id = param}
+
+ minetest.show_formspec(name, "mylandowner:edit",
+ "size[4,4]" ..
+ "field[1,1;3,1;plot;Plot Name;]" ..
+ "field[1,2;3,1;owner;Owner;]" ..
+ "button_exit[1,3;2,1;exit;Save]")
+ end
+})
+
+minetest.register_on_player_receive_fields(function(player, formname, fields)
+ if formname ~= "mylandowner:edit" then
+ return false
+ end
+
+ -- Load information
+ local context = land_formspec_context[player:get_player_name()]
+
+ if context then
+ minetest.chat_send_player(player:get_player_name(), "Id " .. context.id .. " is now called " ..
+ fields.plot .. " and owned by " .. fields.owner)
+
+ -- Delete context if it is no longer going to be used
+ land_formspec_context[player:get_player_name()] = nil
+
+ return true
+ else
+ -- Fail gracefully if the context does not exist.
+ minetest.chat_send_player(player:get_player_name(), "Something went wrong, try again.")
+ end
+end)
+{% endhighlight %}