PencilCoder

Teacher's Guide: Images!

Overview

This lesson shows students how to add images to sprites using the wear function. Additionally, it introduces the slide and mirror functions. slide is an indispensible alternative for moving sprites about the screen in a manner that is consistent with their orientation. mirror reflects images about their primary axis to give the appearance of an image facing the other direction.

More about the lesson

Image files

The lesson begins with examples of the Pencil Code image-search feature because it provides a very low floor for getting started with image-sprites. Additionally, the t- syntax highlights the goal of seeking images with transparent backgrounds.

The drawback to Pencil Code's automated image search is that it does not consistently provide satisfactory results. Students will find greater satisfaction working with images if they can make use of images they identify on their own. This involves more work, but the payoff is well worth the effort.

As illustrated in the lesson, to load a file stored on the web in Pencil Code programs, point to the URL (Uniform Resource Locator, i.e., the "web address") of that image. Because URLs of image files on the web are often quite long, it's advisable to use variables to reference the image file, as illustrated below. This will benefit the script's readibility:

 dogURL = "https://pngimages.com/images/high/cute-dogs-png-bkv41-qzmn9oik8y4u2gij.webp"

 wear dogURL, 200

A prerequisite for successfully referencing image files via a URL is that the reference file must be an actual image file. The names of such files will typically end with an image file extension, such as .png, .jpg or .webp. A notable exception to this are URLS for files from Google Photos, which consist of a mostly cryptic gobbledygook of letters which does not reveal the file type.

Students will likely be tempted to identify images using a general web search, but that should be discouraged, as it is unlikely to yield fruitful results. For starters, owing to the proliferation of websites designed to sell images, the top hits on generic web searches for images will often point to inaccessible images. Either the image URL will be masked, or the URL shown will not be the actual file URL, but a link which directs the remote server to return a whole web page rather than the single image file by itself.

However, there is an even bigger obstacle. Efforts to access files directly from most third-party websites via the wear function are often complicated (if not completely thwarted) by a web browser security feature known as the Same-Origin Policy. This default security rule blocks cross-domain requests, i.e., attempts to load resources such as images that are stored on a separate web server than the one delivering the content to the end user—exactly what we attempt to do when loading an image from a third-party website!

Pencil Code tries to work around the default Same-Origin Policy by implementing a more relaxed Cross-Origin Resource Sharing (CORS) policy. However, for Pencil Code's settings to work, the server the images are pulled from must have compatible CORS policies. Most web searches for images will lead to sites that have incompatible CORS policies, so there's little point to going that route.

The easiest option for beginners is to reference files posted on CORS-compatible image sharing services. There aren't many, but thankfully these should suffice for this lesson. The few that have worked best in recent years include pngimages.com, openclipart.org, and commons.wikimedia.org. Students can simply navigate to these sites and search for the images they want.

Wikimedia Commons is by far the largest of the aformentioned CORS-compatible repositories. In fact, it is the same repository that Pencil Code draws from when searching for images. However, the search tools on the Wikimedia Commons website are rudimentary and frequently yield disappointing results.

Fortunately, it is possible to search Wikimedia (or any other specific site) using the much more robust Google Images search engine. To do this, begin by navigating to images.google.com and enter your desired search term, followed by site:commons.wikimedia.org, e.g.,

After the search is completed, you can subsequently narrow the results using options under the tools dropdown menu. Of particular importance is the color option: select transparent to limit the search to images with a transparent background.

Perhaps the most reliable approach to is to upload images to the web, such as to a personal account on an image sharing site such as Imgur.com or to a public album in Google Photos. Both of these services have compatible CORS settings, which make them an ideal choice. Morever, because the student uploads the files themselves, their image file references will remain valid as long as they want. In contrast, when making use of images on other third-party site, you never know when that image might be removed, causing your program to subsequently fail. And, of course, students then have the option of uploading images of their own (though they should refrain from uploading images of themselves or of their friends, as such personally identifiable information should not be saved on the Pencil Code server).

Instructions for uploading images are provided in the Beyond the Lesson section of these notes.

slide

The slide function is introduced in this lesson as an indispensible tool for moving image sprites around the screen in a manner that agrees with their natural orientation. slide also implicitly allows students to draw based on relative coordinates (the focus of a subsequent lesson) which facilitates various drawing tasks, such as constructing triangular shapes of any proportion without using trigonometry.

mirror

The mirror function accepts Boolean values true and false as well as their CoffeeScript aliases, yes/no and on/off. Note that mirror does not appear as a coding block, so it must be typed in.

The fact that mirror takes a Boolean value as its argument provides an opportunity to introduce the Boolean data type. There is no need to give a thorough introduction now, however, as Boolean values and logic will be explored in detail later, in Conditional Logic! and subsequent lessons.

Editing images

Sprites that have images added to them using wear can be drawn on, just like any other custom sprite, using the drawon function. For example, this alternative solution to the SharkTank activity uses images not only for the background seascape but also for the tank, with a porthole cut into it with dot erase, following the same approach as with a custom sprite.

The ability to edit images is also useful for removing excess material from around the edges of an image (i.e., to crop the image), as done in this alternative variant of the illustration of Hairdos. The trick is to fill with color erase after tracing out the region(s) to be cropped.

Notes to activities

Likely the most challenging aspect to these activities is locating images that can be successfully loaded into Pencil Code. In fact, even when students identify an image URL with an appropriate extension (.png, .jpg, etc.), the image may fail to load, owing to CORS-related conflicts, as discussed above. There is no avoiding some trial and error, though teachers can help by keeping an eye out for coding errors (e.g., failing to wrap the URL in quotes) that can easily compound difficulties at this stage.

Additional activities

  • Create an OceanCritter (or find a relevant image) which slides back and forth randomly on the ocean floor, using mirror to ensure that it faces in the appropriate direction as it moves. Also include simple waves, which should move (i.e., slide) back and forth as well.

  • Use slide to draw a parallelogram centered the turtle's current location, based on the lengths of the parallelogram's diagonals (i.e., the center of the parallelogram is at the intersection of the diagonals, which is also their midpoints). Then use this code in a for loop to construct a SlideStar. With creative use of a nested for loop, you can create a FeatheredStar variation.

  • Find images of hair without a face and add it to a sprite. Size and position the hair on top of a drawing or an image of a person or some other creature. Save your script as Hairdos.

    A rich source for hair/wig files image files is pngall.com. However, in recent years that website has become more difficult to use with Pencil Code. It's CORS policy prevents direct access of the files using the wear function with URLs, and the site increasingly includes pop-ups and other annoyances. Students can work around these challenges by downloading image files to their PC, uploading to Google Photos, and referencing from there. However, to make this task easier, copies image files from pngall have been saved to this shared Google Photos album.

  • ImagesInitials: Create a large custom sprite that covers the whole screen and use pen erase as you write your initials (or draw some other design of your choice). Load an image that you want shown behind your initials.

    Note, to make this work, you need to get the order of execution right: you need to create the image sprite first, or else it will end up on top.

  • BrokenScreen: Find a transparent image of broken glass. Incorporate this into a program that simulates a window breaking, or perhaps some other piece of sheet glass—such as your computer screen!

  • Explore the order of adding sprites with a Waves program. Have multiple layers of waves, each moving back and forth, with fish behind, between, and in front of various layers.

  • RecycledSprite: The notes to the Custom Sprites! lesson describe how to save an image of a custom sprite. If you haven't done so already, modify the script for one of your previous sprites to save a png image of it. Then, create a new program which loads multiple copies of that image, setting each to a different size and moving them in an interesting way.
  • PacManPreliminaries: use the images created for the GhostSprites activity (one of the additional activities to the Custom Sprites! lesson), to model motion for ghosts that can be used for a knockoff of the classic arcade game. Coordinate the movements of the sprites so that collectively they appear to be one sprite moving, with its eyes always pointed in the direction of motion. To emulate the movement of ghosts from the original game, you'll likely need to use a combination of hide, show, slide, and sync.

Beyond the lesson

h3 id="BTL-Uploading">Alternatives to wear

Images can be added to Pencil Code programs using a variety of methods. One simple alternative to the wear function is to pass the image URL directly to the Sprite constructor as an argument. While straightforward, this approach does not allow the user to specify how large to render the image onscreen. As a result, the resulting image-sprite's size will depend on the dimensions of the underlying image, in pixels.

Another relatively simple-to-use alternative to wear is the Pencil Code img function (listed under Art in the coding blocks palette). Like wear, img accepts a second, integer argument that specifies the height (in pixels) of the rendered image. img has the advantage of not being affected by Same Origin Policy restrictions, so it facilitates adding a broader range of publicly available images to a Pencil Code program. Despite these advantages, this curriculum favors the use of wear to img, for both pedagogical reasons and technical reasons relating to how Pencil Code web pages are structured. More details about the img function are presented in the Technicalities section of this document.

Accessing images from a shared Google Photos album

As noted above, a reliable approach to include images in Pencil Code programs is to save copies of them in a publicly-shared Google Photos album and to link to them from there. Accessing a Google Photos image involves the following steps:

  • navigate to the shared album,
  • select the desired image by clicking on it (which causes the image to appear on a page by itself), and then
  • right-click on it and select copy image address to get the URL.
  • Copy this URL into your script and you should be good to go.

This process is outlined in this script, which loads the following images:

At is possible to upload images directly to the Pencil Code server, but this requires using some advanced coding features (e.g., special functions known as callbacks, the focus of a much later lesson). Moreover, the Pencil Code server has file size limitations, which often requires modifying file originals before uploaded. Additionally, storage space on the Pencil Code server is limited; while it would be OK to store a few images on the server, the Pencil Code terms of use prohibit uploading picture in bulk. For all of these reasons, this lesson does not introduce this option.

Importing images saved on the Pencil Code server

The notes to the Custom Sprites! lesson describe how to use the saveimg function to save a PNG image of a custom sprite. These image files can be imported in other programs in the same Pencil Code account much like any other image file. Note the caveat: images saved under one Pencil Code user ID cannot be accessed by programs saved under other user IDs.

Accessing an image on the Pencil Code server is illustrated in this script, which reads in an image saved as DiamondStar.png:

The fact that the image file is on the same server as the script permits of relative URLs rather than the full-length, absolute URLs, that students have used thus far. As the term suggests, the file address is specified relative to the location of the current script. In the simplest case, when the image file and the script are in the same folder, there is no need to specify a path at all:  wear "DiamondStar.png" would suffice; equivalently, one might specify wear "./DiamondStar.png".

In the example, however, the image filed is in fact stored in a neighboring folder on the Pencil Code server. In cases such as these, the relative location can be specified using the .. symbol, which indicates the parent folder. The URL "../../09-CustomSprites/DiamondStar.png" instructs the brower to go up two folder levels, where it should find a 09-CustomSprites folder containing the sought-after file, DiamondStar.png.

The use of relative path names is optional; one can continue make use of absolute addresses to access images stored on the Pencil Code server. However, when doing so with images on the Pencil Coder server, the path of the image seen in the Pencil Code account must be modified slightly, replacing the term edit with home. The correctly specified URL should look like this:

  https://hacker.pencilcode.net/home/09-CustomSprites/DiamondStar.png

Image orientation

In addition to mirror, two other functions can affect the orientation of an image shown on a sprite. (This applies both to images as well as custom-drawn sprites.)

grow: Negative arguments to grow cause the image of the sprite to rotate 180°. (Equivalently, this can be described as a reflection about the central point of the sprite.) Combined with calls to mirror, this gives the user the ability to orient an image four ways, as illustrated in this program:

twist: Imported images at times will be oriented in a way that complicates use of movement functions fd, bk, and even slide. For example, when you instruct the image to move forward, it moves up, which may not agree with the orientation of the picture. For images that are "off" by multiples of 90 degrees, the use of slide is a simple solution. However, for images that are rotated by other amounts, even that work-around falls short.

The twist function changes a sprite's orientation by rotating it with respect to its main axis. Note that when it does this, it is the image itself that rotates. The axis itself stays put (facing north if the sprite has never been moved. Subsequent calls to movement functions, such as fd, will continue to function based on this orientation. This is illustrated below, for the image of a frog:

im function

Additional effects of home

In addition to returning a sprite to its original starting position and orientation, calls to home also reset its size and orientation, counteracting the effects of calls to grow, scale, mirror, and twist.

Stack order of Sprites

In the lesson, it is noted that the order of sprite creation determines which is front or in back. This is not the full story. The CSS z-index property controls the stack order of an element. An element with greater stack order is always in front of an element with a lower stack order. The following code puts an image of a frog on top of the default turtle, which, by default, is always on top:

im function

As the snippet above illustrates, accessing the z-index property requires familiarity with JavaScript objects, as well as CSS-property usage in Pencil Code, both of which are still several lessons away. The requisite background on these topics will be provided in the Custom(ized) Objects! and Format Labels! lessons, respectively.

What can go wrong

Image URLs

A web search on images often yields thumbnail previews that are not actually direct links to the underlying image files. Have students make sure that when they copy URLs that it ends with a .png, .jpg, .gif, or .svg extension. If not, they need to click through until they find the actual image.

Pedagogy

Conventions

A convention is the way something is usually done within a particular activity. They are not as strong or rigid as rules, but they are nonetheless useful. In coding, we come across many conventions. A convention we encountered earlier on involves variables. Variable names typically begin with lower case letters and continue with camel case. However, if they are describing a constructor, they begin with upper case letters, and for variables that won't change values—constants—the convention is to use all upper-case letters, with words separated by underscores. These conventions are valuable because they make it easier to read and interpret code, and help us write code that others will likewise find readable.

In this lesson we encouner another convention, relating to the arguments in the slide function: the x (horizontal) value is stated before the y (vertical) value. This convention arguably comes from math. In any event, we will see it again and again as we learn additional functions in future lessons. Moreover, we previously saw a similar, related convention in the Sprite constructor, in which width was listed before height. For example, new Sprite("100x50") produced a sprite twice as wide as it is tall. Use of conventions in this case make it easier to write code, because it is easier to remember the order in which to list arguments. Later on, when writing our own functions and constructors, we will want to be sure to adhere to these conventions as well.

Technicalities

Delays in loading images

Depending on the size of the image file and various network considerations, the process of loading images can cause notable delays in programs, detracting from the user experience, and potentially leading to timing errors in how the program subsequently runs.

In the simplest cases, including those that students are likely to encounter in this lesson, the program will look glitchy at first, as a turtle or a translucent square sprite appears temporarily until the image fully loads and is rendered. In such cases, a simple but effective remedy is to begin with a transparent sprite. That way, nothing appears until the image loads. Alternatively, one can choose to hide the sprite prior to the call to wear, and make it visible again at some later point.

A more serious issue involves the fact that images are loaded by wear asynchronously, meaning that subsequent lines of code can continue to execute before the image is fully loaded and rendered. Not surprisingly, unless appropriately addresed, the resulting timing issues can cause a program to fail. Timing issues of this nature are addressed in subsequent lessons of this curriculum, beginning with Array Destructuring! As those lessons will explain, we can force the call to wear to execute synchronously, and thereby ensure that the program will not proceed to subsequent until the image is fully loaded, by embedding calling it in conjuntion with special functions await and defer:

await wear URL, defer()

An example of this technique is illustrated in this script.

The img function

To add images to a program, Pencil Code provides an alternative to the wear function, the img function. Like wear, img enables the coder to create and size an image-sprite in a single step. For example,

im function

As noted above, img has the advantage of not being affected by the Same Origin Policy, hence it can be used to reference image files on any server directly. However, this course opts not to introduce img, as the source code underlying it is significantly different from the alternatives, with subtle but important implications for the user. Admittedly, to the end user, the differences between the two functions may seem relatively minor. They become an issue, however, because students are unlikely to remember these nuances; and the inconsistencies between the behavior of image sprites created with the different approaches can become a source of frustration when writing more advanced scripts.

Calls to the wear function generate new HTML <canvas> element, analagous to calls to the the Turtle and Sprite constructors. Because the image is added to a <canvas> element, we can subsequently draw on it using standard Pencil Code drawing techniques. Also,

The img function, on the other hand, generates an HTML <img> element with a src attribute set to the image URL. As a result, this image cannot be drawn upon. While this may not typically be a concern, students typically overlook this nuance and thus later can become perplexed when their image sprite does not behave as they would otherwise expect. For example, sprites created with img aren't full-fledged Pencil Code sprites, as they don't automatically have an HTML class attribute of turtle assigned to them. Though this is remedied easily enough (see the Label Recycling! lesson), the necessary step is likely to be overlooked. As a consequence, image "sprites" created with img will not work well in animation queues when used in in conjunction with calls to await/defer. This type of error can be truly perpelexing and hard to debug!

One thing the img function can do that the other approaches cannot is accomodate animated gifs. An example is provided here:

animated gif example

Sprites with images drawn on <canvas> elements cannot be animated, because it is a static drawing. The only way to make it animated would be to write code that constantly redraws new images.

The background-image attribute

Another way images can be added to sprites is via the background-image attribute, a feature of the <canvas> element. In fact, this is how a Pencil Code Turtle's body (i.e., with the moving arms) is rendered. As with images added to a HTML document using <image>, images added to <canvas> elements via teh background-image attribute cannot be manipulated. However, each Turtle's shell is drawn on the <canvas>, and thus you can draw on using the draw function, as the following script illustrates:

turtle canvas example

Base-64 data-URI

Yet another approach for including images in Pencil Code programs is to use the data URI (uniform resource identifier) scheme. Data-URI provides a way to include data in-line in web pages as if they were external resources. An example of a data URI in use is the Pencil Code turtle sprite. It's data URI can be found through inspection of the turtle in the test panel (click on the image for a script that obtains this data programmatically):

animated gif example

Use of the data-URI scheme is explored in several files in this Pencil Code folder. The notes in these files provide links to resources to generate data URIs from image files, as well as instructions for using data URIs in scripts to create sprites.

One drawback to data URIs is they cannot be used with wear, at least not directly; attempts to do so will clear the sprite's canvas, making it invisible. One workaround is to first create an <image> element using img, then pass the reference to this object to wear, as illustrated at the bottom of this script. Alternatively, data URI coes can be used to add an image to an existing sprite by specifying its CSS background-image style property (CSS properties will be explored in the Format Labels! lesson.) However, the drawing features in Pencil Code require the image to be drawn to a <canvas> element, rather than specifying the background-image, so this approach is not without its own limitations.

Uploading Images: FileReader

Files can be uploaded from a user's computer to the Pencil Code server using the JavaScript FileReader object. Use of this feature involves many advanced concepts, including functions, callbacks, asynchronicity, and events. FileReader is therefore introduced much later in the curriculum, first in the Notes to the I/O! lesson and in greater depth in subsequent lessons.