Step 1: Hello, Prodirect Manipulation World!


Welcome to the first lesson for Sketch-n-Sketch. In this lesson, you will:

  • Learn the basics of the Sketch-n-Sketch programming language and interface,
  • Work through a "hello, world" example, and
  • Create your own simple designs.

Let's get started. As you work through the tutorial, we recommend that you keep Sketch-n-Sketch v0.4.1 open in a separate tab or window and try out all the sample interactions as you read. We'll sometimes drop in a "Practice" marker to provide a gentle reminder, and we'll use an "Exercise" marker when you really should stop, drop, and roll over to your Sketch-n-Sketch tab to try out something in-depth.

Warm-up

To preview the basics, let's jump right in and start playing around. The default example that is loaded, called *Scratch*, draws a single, maroon rectangle. Notice that there is some code on the left (the "code pane") and an SVG canvas on the right (the "canvas pane").

First, try clicking and dragging the box around in the canvas pane, and notice how the program is updated to match your changes. Next, hit the Edit Code button, change the string 'maroon' in the code pane to a different color, say 'lightblue', and then hit the Run Code button. Now the canvas is "active" again, so you can click and drag the box around once more.

From these very simple interactions, we see that there are two ways to modify a program in Sketch-n-Sketch: by directly manipulating the canvas, and by editing the source program in the code pane. In the rest of this article, we will describe the basics of how to use (a) the interface, (b) the programming language, and (c) the direct manipulation capabilities.

The Interface

As you've noticed, Sketch-n-Sketch is a tool that runs completely in the web browser. We have tested the tool in Chrome and Firefox in several configurations, but we have less experience using it in IE and Edge. If you run into any issues, please let us know and try using a different browser in the meantime.

Overview of Buttons and Menus

We will briefly describe all of the components of the user interface up front for reference, but don't worry too much about all of the details — you will get plenty of practice throughout the tutorial. For now, the most important thing to remember is what you already know: that a program can be modified either programmatically (in the code pane) or using direct manipulation (in the canvas pane). You may edit in only one of these two modes at a time. The canvas pane has a faint orange border when it is active and a faint gray border when it is inactive.

Between the code and canvas panes is a column of buttons, as well as a dropdown menu that is used to load different examples and working documents into your active editing environment. The Edit Code button turns into a Run Code button when pressed; this button is the passageway for switching between the two modes of interaction.

The Save and Save As buttons work similarly to how they work in desktop applications, and allow you to save your work to be resumed during a different session. The Reload button reloads your active document to the last save that was made. The Undo and Redo buttons allow you to rewind your history of edits, including those changes made both via programmatic and direct manipulation. Finally, the [Zones] button allows you to switch between different direct manipulation modes (more on that shortly).

Below the canvas is an [Out] button that allows you to toggle between viewing the output of your program as a visual canvas (the default) or as a "raw" SVG definition. The latter allows you to copy-and-paste the raw SVG and export your design to, for example, a local .svg file on your machine or into a conventional SVG editor, such as svg-edit.

Saving Files Locally

There are two kinds of examples listed in the dropdown menu: "Built-In" examples that are are provided with Sketch-n-Sketch, and "Local Saves" which are user-created files that persist across browser sessions. Sketch-n-Sketch does not currently provide support for working directly with the file system, so you will not be able to view your work outside of Sketch-n-Sketch. When you first launch Sketch-n-Sketch, there are no local saves to display in the dropdown menu. Once you create your own local saves, they will appear in a labeled section at the top of the dropdown list.

Loading and saving examples should be pretty straightforward. The Save As button creates a copy of the current example, the Save button saves changes to the current (locally saved) example, and Reload discards any outstanding changes since the last saved version, without issuing a confirmation prompt. (Practice 1.1: Now is a good time to try creating your first local example.)

To load a different example, simply click on the dropdown menu and select one. When doing this, any outstanding, unsaved changes to the current example are discarded (again, without warning in the current implementation). If you select a built-in example, the program will be displayed in the code pane and its output will be rendered to the canvas pane. If you select a local save, the program will be displayed but not its output. After all, you may have saved a document that was not complete and would not compile! So, just hit the Run Code button when you are ready.

The last thing to know about the dropdown menu is that *Scratch* is a special built-in example. Unlike for other built-in examples, Sketch-n-Sketch will remember changes to it as you work, so you may switch away from it and then back and not worry about your work disappearing. These changes do not, however, persist across browser sessions. So, it really is just a temporary scratch space for toying around. Usually, you will want to work with locally saved files.

Okay, there's one more thing... we promise this time! If you would like to delete all your locally saved examples, select *Clear Local Saves* from the bottom of the dropdown menu. Be careful, there's no prompt to let you change your mind, and when they're gone, they're gone for good!

Customizing the UI

The interface also allows for a degree of customization. The interface can be toggled between a vertical and horizontal view by pressing the [Orientation] button.

The relative sizes of the code and canvas panes can be changed by clicking and dragging the edges of the middle button section. Whenever you save, your interface layout is also saved for that document and is automatically set when you load that document. Go ahead and fiddle around with these options now.

Programming in Little

Programs in Sketch-n-Sketch are written in an untyped, little, programming language we call Little. We have chosen to use a minimalist syntax in Little that should feel familiar to anyone who has used a lisp-like language. If not, don't worry! The syntax is very simple and will be introduced through a series of examples. If you would like to see a more complete, technical summary of Little, check out this syntax reference.

The general structure of most Little programs is the definition and calculation of everything you need for your graphic design, followed by a final "main" expression that puts together an entire SVG image. The simplest such Little program is the following:

(svg [])

This program calls the svg function to draw a canvas with the given (empty) list of shapes. Practice 1.2: Go ahead and enter Edit Mode while on the *Scratch* example, delete the contents, and type in the code above. Then, run the code. Congratulations, you've just written your first Little program!

Most kinds of expressions in Little, such as the function call in the program above, must be surrounded by parentheses. The parentheses must be included, and extra ones are not permitted. For example, both ((svg [])) and (svg ([])) are invalid Little expressions. Try these out to see how Sketch-n-Sketch becomes very angry when you try to run invalid programs.

A blank canvas is uninteresting, so let's add some shapes. There are several built-in functions for making shapes. For example, the rect function can be called as follows:

(rect color xpos ypos width height)

The xpos and ypos arguments specify the x-position and y-position of the top left corner of the rectangle, respectively; width and height specify the width and height of the rectangle, respectively; and color specifies the color of the shape. The color argument can be specified using any color string recognized by CSS or as a list of four numbers representing RGBA values. (The next lesson will also introduce a notion called "color numbers" in Sketch-n-Sketch.) The position, width, and height values must be numbers. So, all of the following are different ways of defining the same rectangle:

(rect 'Blue' 100 100 200 200)
(rect 'blue' 100.0 100.0 200.0 200.0)
(rect '#0000FF' (+ 50 50) 100 200 200)
(rect [0 0 255 1.0] 100 100.0 (/ 400 2) 200)

In fact, the following list expression is another way to represent the same rectangle without calling the rect function; we will talk more about this "internal" representation in a subsequent article:

[ 'rect' [['color' 'blue'] ['x' 100] ['y' 100] ['width' 200] ['height' 200]] [] ]

Enter Edit Mode, and place the rectangle into the empty svg definition that we have from before:

(svg [(rect 'lightblue' 200 200 300 150)])

Now, run the program. There's your rectangle! Try experimenting with different colors, positions, and sizes for your shape. Also try providing some invalid arguments and see what happens. (Note: We try to handle most errors gracefully and, in the worst case, detect when Sketch-n-Sketch crashes and relaunch the tool in a similar state. However, there are still bugs, of course, but we hope they don't get in the way of your exploration too much.)

Helper Definitions

Adding shapes one at a time is difficult and inefficient for any more than a handful of shapes, especially if the shapes share attributes in common. To aid in this, we can create our own names for structures that we intend to reuse. This is done by defining variables using the built-in def and let expressions. The syntax of these kinds of expressions is as follows:

(def pattern patternDefinition) remainderOfProgram

(let pattern patternDefinition remainderOfProgram)

They are very similar, and differ only in their intended use; def is meant for "top-level" definitions that will be used for the remainder of the program after it, and let is meant for more localized definitions within a def. This distinction will become clear as you become more familiar with the tool. For now, just remember that you cannot put a def inside of another def.

The pattern in the above definitions can either be a bare variable name or a list of patterns, each of which can be a bare variable name or another list of patterns. So, the following definitions all assign the values 10, 20, and 30 to the names x, y, and z:

(def x 10)
(def y 20)
(def z 30)
(def [x y z] [10 20 30])
(def [[x y] z] [[10 20] 30])

These variables can also be defined using a sequence of nested let-expressions, as follows:

(let x 10

(let y 20

(let z 30

  restOfProgram

)))

Notice how the parentheses "build up" at the end. Yet another way of writing the same expression:

(let [x y z] [10 20 30]

  restOfProgram

)

Let's put these to use by giving a name to our rectangle, then using that instead of the function when we put it in the call to the svg function. One way we could do this is as follows. Note that single-line comments in Little are specified using the semi-colon character.

; Top-level definitions
(def myRectangle (rect 'lightblue' 100 100 60 130))

; Main SVG expression
(svg [myRectangle])

Much better than before! Try using let instead of def and notice the difference.1

Function Definitions

Let's now try defining several rectangles that look the same, evenly spaced in the horizontal direction. One approach is the following:

; Top-level definitions
(def myRectangle0 (rect 'lightblue' 100 100 60 130))
(def myRectangle1 (rect 'lightblue' 250 100 60 130))
(def myRectangle2 (rect 'lightblue' 400 100 60 130))

; Main SVG expression
(svg [myRectangle0 myRectangle1 myRectangle2])

Although this does, in fact, draw three evenly-spaced rectangles, the copy-and-pasted code makes it difficult to change the overall program. What if we wanted to change a single property of the pattern, such as the color of the boxes or the separation between them?

Exercise 1.1: To demonstrate the problem, try changing the height of all three boxes, and then change their positions so that they are spaced 274 pixels apart rather than 150. After that, add a fourth, identical, evenly-spaced box.

As structured, our only option for such changes is to modify multiple places in the program. Yuck! Much better would be to refactor the program so that the common attributes are written once and to define a function that computes the ones that differ.

The syntax for defining a function (also known as an "anonymous function" or "lambda" if you're familiar with functional programming) is the following:

(\argumentPattern functionBody)

The backslash character is what indicates that this Little expression (which requires parentheses, like most expressions) is a function definition. This function expression takes a single argument variable or pattern. To define a function that takes multiple arguments, write the following:

(\(argumentPattern1 argumentPattern2 ... argumentPatternN) functionBody)

The arguments are each defined in terms of patterns and multiple arguments are contained in parentheses and separated by spaces. The names for these arguments can then be used in the following expression. For example, a function that takes in variables x and y then returns their sum would be as follows:

(\(x y) (+ x y))

Functions are often bound to a variable using a def (or let) so that they can be referred to by names.

So, to define a function placeRectangle that places each rectangle at a given x-position, we could write the following:

(def placeRectangle (\xpos
   (rect 'lightblue' xpos 100 60 130)))

Notice how placeRectangle takes an argument for the sole attribute, namely, the x-position, that is different among the three boxes above. Now, we can finish the program by rewriting the "main" expression from before to the following:

(svg [(placeRectangle 100)
      (placeRectangle 250)
      (placeRectangle 400)])

This version is much easier to modify because a lot of the boilerplate code has been eliminated.

Mapping Over Lists

One more thing we can do to improve our program is to eliminate having to explicilty call placeRectangle each time we want another box. Instead, we can apply the map function, which takes the following form:

(map function list)

What map does is apply the function to each element in the list, and returns the list consisting of the results.2 So, we can define a name that represents many rectangles and then use that instead of writing a whole bunch of names. We could do this in the following manner:

(def placeRectangle (\xpos (rect 'lightblue' xpos 100 60 130)))
(svg (map placeRectangle [100 250 400]))

This concise program describes three similar rectangles, and if, we want to change any of the shared attributes of the rectangles, like their color, we only need to change one definition as opposed to three. Note that there is no longer the list brackets around the argument to svg, because the output of map is already a list.

Exercise 1.2: Redo the tasks from Exercise 1.1, this time starting from the program above with placeRectangle and map.

Direct Manipulation

Although we hand-rolled the Little language specifically for Sketch-n-Sketch, it's a pretty standard untyped, functional language extended with support for SVG images. The major novel component of Sketch-n-Sketch is that the editor allows you to directly manipulate the output generated by a program.

If you haven't already tried, go ahead and try clicking and dragging the boxes of the output after running the code and observing what happens to the input source program. To see all of the ways you can manipulate your shapes, toggle the Zones option from Hidden to Basic. Even when hidden, the basic zones are active and can be manipulated.

Each zone is tied to particular attributes of a shape. For example, the "Interior" zone of a rectangle is tied to its x- and y-positions, and the "Bottom Right Corner" zone is tied to its width and height. As you directly manipulate a zone, Sketch-n-Sketch figures out what to modify in your program to match your changes, all in real-time!

Try dragging the interior of one of the boxes. Notice how the appropriate x-position in the program is changed when dragging the mouse to the right or left. More interestingly, the y-position of all the boxes change when dragging the mouse up or down. This is because our program was defined such that all rectangles have the same y-position (for example, 100, in the version written above). So, in response to your change to one rectangle, Sketch-n-Sketch figures out that the only option is to change that constant, which (after re-running the program) changes all the rectangles. In this way, Sketch-n-Sketch modifies your program without altering or "breaking" any of the high-level structure of the program.

This kind of synchronization between a Little program and an updated SVG canvas can become subtle as programs get more complicated. As a way to help you understand what effects your direct manipulation will have, Sketch-n-Sketch uses the following color schemes for syntax highlighting. Whenever you mouse over a zone, the constants that will be changed by manipulating that zone are highlighted in yellow in the source code. When actually manipulating that zone, the constants that are being changed are highlighted in green. Constants that went into the calculation of that attribute but will not be changed by your manipulating them are highlighted in gray and constants for which changes could not be deduced are highlighted in red (more on the meanings of these last two colors in later lessons).

Exercise 1.3: Different shapes come with different attributes that can be manipulated. Redefine your placeRectangle function to place a circle instead, and then examine the different zones that are available to you. As a reference for what the circle function takes as arguments, take a look at Prelude, a library of Little functions that you can use. The comments above each function serve as documentation. If you would like to see more examples of Little code, you can also dig in through these definitions themselves. Note that you can also view this library in the "example" called *Prelude* in the dropdown menu.

Summary

Whew! You now should have a sense for how to specify your own shapes, names, and functions in Little.

Exercise 1.4: To further exercise your knowledge, go ahead and perform the following tasks before proceeding to the next lesson:

  • Change your placement function to take in the y-value of the shape instead of the x-value. This should result in your shapes being placed in a vertical line instead of a horizontal one.
  • Change your program to display more than one row of shapes using any means you see fit.
  • Change your placement function to take an x- and a y-position and use these to make a grid of shapes. Hint: The function can be mapped over a list of points, such as [[100 100] [200 100] ... ]. If you take this approach, then your mapping function should be defined with the form (\[x y] ...) — notice the square brackets instead of the parentheses we have seen before — which "extracts" the two values from each point in the list.

Next Up: Step 2


  1. To get a sense for why the def syntax was introduced, see the end of this old version of Prelude, where there are a lot of functions in play.

  2. Just like in functional programming languages like Elm and Haskell, and similarly to the map method in JavaScript.