Tuesday 25 August 2009

Dynamically generating SVG in an HTML page

In this post, I want to briefly list the less obvious aspects of using SVG to draw a game board. These are notes from my initial implementation so there may well be more polished alternatives.

For this work I used a recent version of Firefox and I have not yet investigated other browsers in any depth so your mileage with other browsers may vary.

My work was aimed at dynamically creating a page (hence the use of Javascript) but the same techniques could be used to create a static HTML page including SVG.

This project uses XML, SVG and Javascript as well as simple HTML. I have not provided any links to information on these subjects; I recommend finding a good book on XML and Javascript but a simple web search generated enough information on SVG.

Embedding SVG in HTML

I did not want to just have a stand-alone SVG page but an SVG picture embedded in an HTML page. This is because I wanted to make use of HTML elements and I also found HTML better for loading Javascript than stand-alone SVG.

I found that I needed to create the HTML page as an XML page so the start of the page is as follows:


<?xml version="1.0"?>
<html xmlns="http://www.w3.org/1999/xhtml">
xmlns:svg="http://www.w3.org/2000/svg"
>
<head>


This provides both a default XML namespace for the HTML (actually XHTML but don't worry about that) and a namespace for use by SVG elements. This is important as the browser needs to be able to tell whether any element in the page is meant to be HTML or SVG. We will come back to namespaces later. If you are not familiar with XML namespaces then it would be worthwhile brushing up on them but the examples below include all the code necessary.

When I served this from Django I had to ensure that the HTTP Response had the correct MIME type as follows:

return HttpResponse(t.render(c), mimetype="text/xml")

If you are not familiar with Django then don't worry about this line, it just returns the page but with the MIME type set to text/xml.

Within the HTML body I simply embedded the SVG picture directly. I did not include it in any explicit graphical or other object.


<svg:svg id="board" width="500" height="500">
<svg:rect x="10" y="10" width="485" height="485" fill="#FF9933">
</svg:svg>


Note that the elements all use the svg namespace with the shorthand reference defined at the head of the file. We need to give the svg element an id so that we can refer to it from Javascript.

I found that I had to set the width and height of the svg object. I had hoped to have it more dynamically sized but that did not work too well. Although I deliberately did not set the units of the svg element, I found that it ended up using pixels as the units. I was hoping to avoid setting the size in pixels in order to make the page work well with different sized screens and windows but I was unable to make it scale well. I could have investigated further but I chose to pick a plausible size instead. I may return to this at a later date.

In the example above, I included a rectangle. I could have just drawn it dynamically but it helped me during development to ensure that the SVG was drawing properly before I started creating more elements.

If you just put these elements in a HTML page then you should get a page (which you can put any normal text, images etc. as standard HTML) with a picture embedded in it. The next section covers adding more drawing elements.

Drawing SVG elements with Javascript

In this section, I assume that you are already familiar with using Javascript to generate HTML elements and dynamically add them to an existing page. This type of DHTML is covered by many articles online but the basic concept is to create a new element object of the required type, set any attributes and then to append it as a child element under a known location. The code below uses the same technique but with an extra wrinkle for SVG. If your Javascript DHTML knowledge is limited to copying inner HTML then this is the next level but it is not too difficult.

For my purposes, I knew that I was going to create a lot of SVG elements so I wrote a function to create an arbitrary SVG element.


function createSVGElement( eltType, properties) { // Create an arbitrary SVG element
var tgt = document.getElementById("board");
var elt = document.createElementNS('http://www.w3.org/2000/svg', eltType);
for ( prop in properties ) {
elt.setAttribute(prop, properties[prop]);
}
tgt.appendChild(elt);
}


This code has three parts:

1) The createElementNS() function is used to create a new element. If you have worked only in HTML then you may have used the CreateElement() function. This version adds a namespace argument which we need in order to specify an SVG element. Without this, the browser will not recognise the element, even if you create an element with a name that is not valid in HTML.

2) The attributes of the new element need to be set. These attributes will include geometric attributes for location and size as well as fill color etc. I used the array approach simply because of wanting a re-usable function. If I was using inline code to create the elements then I could use SetAttribute() as above or use the dot notation. If you use the dot notation to set attributes then be aware that some SVG attribute names are not legal Javascript names (they include a dash). For these attributes, the SetAttribute() method is necessary as it uses the property name as a string.

3) Finally, the new element is appended as a child to the top-level svg element. This is just the same technique as is used for dynamically adding HTML elements to an HTML page. In my example, I added every element directly under the root svg element but there is nothing to stop you from creating a less flat element structure. This might be particularly useful if you want to try changing properties of grouping elements for dynamic effects.

Given this function (and I can already see at least one way of optimizing it), here is a chunk of code that will draw a simple line.


var lineProperties = {'stroke': 'black'};
lineProperties.x1 = 10;
lineProperties.x2 = 10;
lineProperties.y1 = 10;
lineProperties.y2 = 490;
createSVGElement( 'line', lineProperties);


This chunk is slightly clumsy because it has been taken from a more dynamic function. The lineProperties object is initialized with the line colour and then properties are added for the x and y of each end.

Note that I have used both the string way of setting a property and the dot notation (which is fine for these properties). In this case, I could have just created the properties in one statement.

Given the set of properties, we simply call createSVGElement() with the name of the element and the set of properties.

Full details of the properties of SVG elements can be found in the SVG specification.

Associating events with SVG elements

The code above shows the techniques required to draw an SVG picture within an HTML page. In my case, I wanted to draw a Go board and I was simply able to draw the lines of the board with a for loop. However, in order to make this part of a game, I wanted a way to capture input events. SVG elements support an onclick() attribute which is a reference to a Javascript function. This is set in the same way as any other attribute.

While using onclick(), I used some other techniques. The first is to remember to set the pointer-events attribute (remember that I said that some SVG properties have a dash - this one cannot be set using the dot notation).

The second is that it is possible to have a hidden element that can still capture pointer events.

The third is that I set the id property for each pickable element and I built the id into the onclick() event as an argument. This means that each pickable element can identify itself when selected.


var circProperties = {'visibility': 'hidden', 'pointer-events': 'all', 'r': '10'};
circProperties.id = 'circ_10_10';
circProperties.cx = 10;
circProperties.cy = 10;
circProperties.onclick = 'playMove("'+circProperties.id+'");';
createSVGElement( 'circle', circProperties);


In my real use of this example, the cx and cy and id properties are set from loop variables.

Unfortunately, SVG elements do not have drag events. A simple click event is fine for Go but less than ideal for some other games. For example, in order to implement a game of Chess where the obvious user action would be to select a piece and drag it to its new position, I would need to select a piece and then select a new position. This requires some state in the Javascript and is less obvious for users. One thought is that, when a piece is selected, I could highlight the legal positions that it could move to. This requires the game logic to be embedded in the Javascript rather than at the server side but it would not be too hard.

Alternative Approaches

As I mentioned above, I did this work in Firefox. I like Firefox but I found that SVG support in IE and Konqueror is very limited (i.e. I couldn't make any of this work but I didn't try for more than a few minutes). However, A comment to my last post from Brad Neuberg pointed out that the SVGWeb project (http://code.google.com/p/svgweb) should add SVG support to IE and other browsers. I have not had time to try this but I will definitely give it a go.

However, I have also considered other approaches. One of these is to generate a picture file and lay it over an image map in straight HTML. This requires generating the complete picture at the server side and then sending the picture file to the browser. This picture file will definitely be larger than fragments of XML containing move information and the server will actually have to generate the picture. I have dabbled with generating a PNG file from Python and it can be optimized quite heavily but it is still less efficient than using SVG. However, for compatibility reasons and to see just how difficult it is, I will probably try it out at some time.

Monday 10 August 2009

Thoughts on a Browser-based board-game

One of my spare-time projects over the last few weeks has been a browser-based, pure HTML and SVG implementation of the Go board game. This has reached a usable state (although definitely still pre-alpha) and is worth some reflection on the architectural implicatons.

My starting point was that I wanted to build the game using a Django / Python back-end and HTML / AJAX / SVG front-end. The project came out of my experimenting with Django and I am considering other types of game but Go was a good starting point.

A more commercially realistic approach would be to build a Java or Flash client but I wanted to see how much could be achieved using SVG. Also, I dislike Flash (due to its over-use) and I am tired of problems with Java installers.

Client Implementation
The client worked fine once I read up slightly on Javascript. Directly creating SVG objects has a namespace quirk but otherwise works very easily. I chose Go because the graphics are easy and the game-play just involves selecting points. My experiments have shown that it would be straightforward to use graphics files for the board and pieces if I wanted a more attractive appearance.

The two drawbacks that I have found with this approach are browser support and the lack of drag and drop.

My early experiments with IE and Konqueror show poor SVG support so I just worked in Firefox for now. If I persist with the project longer term then an alternative would be to draw the board and pieces server-side into a PNG file and send that combined with a client-side image map. This would be more work on the server side and would require more data transfer but I would quite enjoy creating a basic PNG library in Python.

SVG includes onClick events so I was able to put hidden items on the points to be selected. This works fine for Go but would not work in, for example, chess where a drag and drop action would be desirable. It would be possible to click once to seect the piece and then again to select the destination but I will have to try it out to see if it feels too clumsy.

Server Interface

I was deliberately working with a Django back-end intended for ease of deploymen so I wanted no non-standard server features. this proved interesting as what I initially considered to be a clumsy architecture turns out to have some attractive aspects.

Dedicated server versus HTTP transactions

If I had not been basing the design on Django then I would have started with a server that kept each active game in memory and applied moves as they were received. As I was using Django, each browser action was totally separate and so the POST request to play a move requires the game to be loaded from the database, the move applied and a response sent to the browser with captured stones etc. and the game saved in an updated form.

In a quick-moving game, having to load and save the game for each move is an overhead but when a longer-term view is taken the Django approach (really , the generic web-server approach) has robustness advantages. With a server running direct connections to clients I would have to work out what to do if a connection went down - how could a client re-connect and resume their game. Also, what if players want to pause a game overnight? Basing the whole approach on an atomic move basis automatically handles these aspects and also provides a re-play feature almost for free.

Server Push

Another aspect that was less convenient was trying to implement server-push. I want to be able to update player A's browser when player B makes a move (I also included a chat pane in the game which has the same requirement). In a dedicated server, I would use a long-standing read from the client and would provide a practically instant response. This is not really workable using an HTTP server.

In theory, I could use a dedicated HTTP GET request to fetch updates and have the server delay replying until there is event data to return. On the browser side this should work fine. Unfortunately, it has two problems on the server side

Firstly, it means that each game would involve two dedicated requests outstanding at all times in addition to the transient requests to fetch game state, post moves etc. With conventional HTTP servers this would likely cause a scalability problem as each connection requires resources and servers are configured to limit connections.

Secondly, HTTP servers running Django or other CGI frameworks keep each request separate (for very good reasons). Therefore, if I had a pending request for player A and a separate POST from player B it would be tricky to communicate between those requests. This might not be insuperable (and would be trivial in a dedicated server) but I did not investigate further given the resource issues.

This means that my event handling is based on poll-based requests that mostly return no new event data. This is fine in terms of server resources but it does consume network bandwidth and means that the responses can be delayed for a few seconds. This type of problem has been met by a lot of other people (look up Comet in Wikipedia for example) and I expect that I will return to it to investigate further at some time. For example, at what point does the server load from repeated requests with no events outweigh the resource cost from outstanding requests and how can I pass events from one request to another (maybe the new Django signals will help)?

Implementation Efficiency

One lesson from this project so far is the ease of use of Django. Because Django takes care of all of the plumbing, I have 200-300 lines of Python to implement the rules of Go, about 100 lines of plumbing and chat code in Python and about 200 lines of Javascript to draw the game and handle moves. The whole project took less than a week of evenings so far and that included a chunk of learning. Adding more error checking and more attractive admin interfaces will probably double the size but the whole project is easily understandable and manageable.