I like to write code, especially code which presents things like web pages, and in particular code that writes web page code. I like to draw and write fiction as well. So it occurred to me that an application to share comic books or other sequential documents created from my (and other’s) content would be useful, and certainly fun to write. That is the impetus for the Storybook Comic Book Builder.The Builder is an accessible Content Creator Application that in turn creates accessible documents. We have set the following requirements for the Builder, that the comics or documents it makes
- use a mobile-first responsive design
- are very quick to load
- are accessible by WCAG guidelines
- have semantic content and logical syntax
- are easy to navigate using only the keyboard
- support GIF or WebP animations and MP3 audio content
- are portable to run on various media (eg thumb drives)
Based upon the evidence presented by the evaluation results from running these tests
On our example comic as shown in these screen shots snips above, it would be fair to say that the Builder is relatively successful in addressing the areas of accessibility, semantics, and SEO. The Web Content Accessibility Guidelines (WCAG) resources linked in the document and others were used as a basis for informing our expectations of the user provided content and how we present that content in an easy to navigate accessible fashion. In addition to addressing those areas relative to accessibility, a good web page is going to be mobile-first responsive and fast.Why mobile-first? Simply that the large majority of web traffic today is on mobile devices. On average your likely visitor is on a mobile device. However, mobile-first should not mean that a page is unfriendly to other devices. The key is obviously to cater to your audience whatever device they may be on. Our example comic achieved these results from the Google “Mobile Friendly” evaluation.
Another important consideration is the speed with which our image content is loaded and displayed. In fact, amazing as it may seem, people will abandon their visit to a site they deem to be slow or unresponsive! Usually the major contribution to the download speed is the size of the downloaded content. That can be a significant factor on a cellular network as an example.When we look at methods for the content presentation of web pages, especially image content, we are likely to encounter the concepts of adaptive and responsive design. For our purposes we will call a method that serves different content based on viewport dimensions as being an “adaptive design” while a method that modifies or scales the served content will be called a “responsive design”. In a comic much of the content is typically images, and one aspect of the quality of the comic (besides the content itself of course) is the quality of the presentation of those images.The remainder of this article will focus on a method that synergistically uses both adaptive design and responsive design to accomplish a fast responsive image download and display. All of the code for the Builder along with our example comic is available from a but we will limit our discussion to those portions relevant to our image display method. The example comic can be viewed at , and there are also some other comics in our
Image Selection as Adaptive Design
The Image Selection HTML
There is a relatively new paradigm for image selection in HTML5 and that is the notion of a “source set” attribute for an image, and the way that this is accomplished is basically to present the browser with a list of images that it may choose from. This idea of choosing an image from a list is of course demonstrative of “adaptive design”. In general the list may offer a number of image sizes listed by their width and it may also offer a number of image formats. A list of media breakpoints is also provided to assist the browser in making its choice. This sample shows how this HTML code appears in the comic web page as generated by our PHP builder code.
As can be seen in the code sample, there are five distinct image width choices in two image formats along with five distinct media width selections, and we can see that the image width values and the media width breakpoint values correlate. The values of these dimensional choices were determined based upon the set of
- X-Small, Less Than 576px
- Small, Greater Than or Equal 576px
- Medium, Greater Than or Equal 768px
- Large, Greater Than or Equal 992px
- X-Large, Greater Than or Equal 1200px
- XX-Large, Greater Than or Equal 1400px
The basic premise of how this source set scheme operates is that the server provides our browser with HTML similar to our example, that is HTML that includes a list of choices for image content. Then our browser selects the appropriate image from the list and downloads just that image for display. The source list is ordered by our nominal preference among the images, and the browser stops analyzing the image list once it finds an image that satisfies its interpretation of the display environment. In our case we order the list by image format and image width in pixels.The five image size selections are presented in both WEBP and JPG format filetypes, and the filenames for a particular size image are the same in both file formats except for the filetype extension. In general there is no visual benefit to serving images that the browser has to downscale to display, and an image that is twice the size of the viewport is four times the size of an image that just fits. Images that must be upscaled for display frequently suffer in visual quality. It is usually better to serve images that approximate or that are slightly larger than the viewport dimensions. The image widths based on the Bootstrap breakpoints seem to provide an excellent range and granularity of dimension choices for the vast majority of use cases.The WEBP format is relatively new but it has been widely adopted and has some substantial benefits. On the average converting our comic JPG images to WEBP images with the default compression setting (75) has reduced the image file size of the resulting WEBP to just over 10% of the JPG file size with no obvious visual degradation. Obviously this will substantially improve download speeds, and since we have listed the WEBP format images first the browser will default to that format if it is supported.Now let’s look at how the Builder generates this image element and its contents, especially the source set list.
Creating the Image Element’s Source Set contents
The Builder application that creates the comics is written in PHP (our site runs version 7.3) and as was previously stated, all of the source code and an example comic along with some explanatory documentation is presented in a . The source code itself is contained in a repository subfolder named There are quite a few program files in that folder, however for our discussion we will only need to reference a few specific programs. The image display concepts we will expose in those few programs are also reused or adapted in some of the remaining code.The program is the landing page for the Builder application. It largely consists of a list of forms that gather user input to obtain, for example, the name of our comic. Most of this information is used to populate a "card" for our comic which we display in our . Besides this function however, the index.php program also calls subprograms that create the folders that will be used for our comic content. So for our example comic, "Hyenas", index.php creates a folder with that name under the site Comics folder within the document root. After this is accomplished, the index.php program will then proceed by turning control over to a sequence of programs that each allow the user to select and upload the content that will be used to generate the comic.The image in our example was selected by the program. This program in turn leverages a lovely JavaScript program I found some time ago to sort and display a list of image files to be uploaded. The script also uses an AJAX call to in turn invoke the program which validates the uploaded files and constructs the image files listed in our image element content. The JS file is named and its author has a site at to share their work. However, while I am fairly certain that this is the source of the code I adapted, I cannot locate that code on the blog site. Meanwhile, the script itself can be found on the Internet and without attribution. I said lovely just now, and that aspect of the code influenced my approach to code, so the author gets “shout outs and props”, as well as attribution. Here is a link to his blog: The getComic.php and vpb_uploader.js scripts basically serve as part of the user interface to the Builder while the actual content evaluation and creation is accomplished by the imgUploader.php code. These scripts pass each file to be uploaded to the imgUploader.php program one at a time in turn by an AJAX call for each file. The first 120 odd lines of the imgUploader code invoke the SESSION handler and set some variables, then the code checks to be sure we are here by a properly constructed POST call before it does checks for upload success and the image file size and file extension and the filename as well as to replace spaces in the filename with underscores.The Builder supports the upload of image files of filetypes JPG, PNG, and GIF. The first thing we do with an uploaded JPG image that passes our initial checks is to regenerate it using the PHP image functions, which serves to help “sanitize” the file. PNG files are converted to JPG format using the PHP image functions. However, while the Builder supports GIF images and also generates a WEBP alternate format file for the uploaded GIF file, it does not scale or generate a source set for uploaded GIF images. I anticipate adding this resizing facility by using the imagemagick utility and the imagick PHP extension. Stay tuned!Besides the present exclusion of GIF filetypes the comic has three other categories of “non-content” images that are not presented by a list of sizes in an image element source set. One such category is the image used for the gallery card, another is any logo images, and finally the OG image. Our interest in the image element source set as a list of sizes is for “content” images, for example the images that are the comic’s pages. Based upon our previously discussed breakpoint and image size choices, the optimum uploaded image size will be 1400 pixels in width. In this code segment shown below we can see that the program takes the sanitized and verified JPG upload and scales it to a 1400 pixel width if required. The JPG is then copied as a WEBP format file of the same dimensions. This gives us the two largest (dimensionally) files in the source set list.
In addition to the scaling and conversion functions much of the code in this first segment serves to generate a uniquely constructed name for this pair of images. The image name syntax we use allows us to include some information about the image in its name. In this first segment we have appended a -X- to the base filename and then further we have appended a formatted string for the image height, and we append the same string to both the JPG and WEBP filenames. The scaling and conversion and copy functions along with the naming generation could well be accomplished in a function call, but in our example we have instead embraced the simple expedient of a “step and repeat” construct. So this segment shown next is virtually the same as that above save for the width dimension we seek to change (1200 instead of 1400) along with the appropriate changes to the filename suffix. The program replicates this segment slightly modified five times, once for each of the image sizes we wish to serve. Thus at the bottom of this scaling and conversion stack we will have a collection of ten images that are five sizes in two formats. You may note that some of the code in these segments generates “report entries” which we can display with a dumpsession.php utility program included in this GitHub .
Once the images are available the imgUploader.php program continues by placing the image files in an appropriate folder and then updating a mySQL database to reflect the newly added image before finally returning to the vpb_uploader.js caller. The code to implement these two tasks is shown in these next two program segments.
At this point in the Builder process we have created the Image Element’s Source Set contents for a comic image, and this process is repeated for each image or page of comic content. When all of the image and other comic content is finally gathered we can then generate a comic with this content as we discuss next.
Creating the Image Element with its Source Set contents
When the makeComic.php script starts it first checks to see that it was invoked by a proper POST request. If so it starts the PHP SESSION and loads some variables including font choices particular to our comic. The script then sets up a buffer to contain the comic HTML that it will next generate. The Builder can generate some fairly complex documents or comics that can include text captions, alternate images, animations, and audio. The makeComic.php program is about 800 lines of code but our focus here can concentrate on a small part of that total.You may recall that the imgUploader.php script we discussed earlier creates and saves the image files that will be listed in the Image Element Source Set, and that it also saves some information about each base image in a mySQL database. After the makeComic.php script generates the HTML Head section and some embedded CSS for our comic, it uses the database contents to obtain information in turn about each base image. The PHP class that we use as our database handler is included in the code repository for those who want to take a deep dive. This next code listing shows the procedure we use to loop through the database to process each page image.
This next code segment shows how we parse the folder directory of image files we created earlier with the imgUploader.php program to extract an array of image file names that we subsequently use to generate an Image Element containing the Source Set attribute as we showed in our earlier code listing example.
So we have examined the methods we use to generate the comic HTML Image Elements and their included Source Set attributes. Our generated base HTML uses this construct to present an adaptive image display for the comic contents. In and of itself the HTML5 Image Element Source Set method we have described here does a fairly decent job of serving and displaying images sized appropriately to our viewport dimensions.But now let’s consider that the Source Set image list is ordered by width, and that the browser selects an image based on this dimension. Now let’s further suppose that the content image has a portrait or vertical aspect ratio and that we are viewing the image on a landscape or horizontal aspect ratio device. We can see that this might result in an image that is taller than our viewport, an image that would be cropped. Of course a similar problem can present with landscape content on a portrait device. For our choice of content which is largely images, that is comics, it is likely better to size the page to fit within the viewport while maintaining its original aspect ratio. This next section of our discussion explains how we use some JavaScript code to appropriately scale the image that was chosen by the Source Set HTML such that it just fits within the device viewport while preserving the image aspect ratio.
Image Scaling for a Responsive Design
The comics that the Builder creates use a Javascript program named to modify the Source Set adaptive image choice by scaling it responsively. The Reader.js script itself is just over 400 lines of code, but that part of the script used to do our scaling functions is not very long or complex. A key part of the script’s scaling function depends upon its knowing the dimensions of the image it is expected to scale. This next figure illustrates how the browser exposes its image selection (along with some other information about the image display) as may be viewed in the debugger.
The browser exposes the name of the Current Source Image file as “currentSrc”, and this segment of Reader.js shown next illustrates how we parse that name to obtain our image dimensions. You will recall that the image name generated by the imgUploader.php program encodes these dimensions in the filename. When this code segment completes we will have the image filename along with its width and height dimensions. Note that these dimensions will be the same as those shown as the image “Intrinsic size” in the debug popup in the figure above and which for our example browser selected image file are 1400 x 1575 pixels. The viewport dimensions for our example browser meanwhile are 1150 x 1258. Why the browser chose this source image from the Source Set list I cannot say. However, and most importantly, this is the image that was downloaded to our browser.
The next thing our Reader.js script does is a ratiometric scaling of our downloaded image so as to just have it fit within our viewport while preserving its aspect ratio. This is a fairly straightforward process as is shown here next. Once the new scaled dimensions are determined, the script writes these dimensions as width and height Image Element attributes, and then writes the image filename as the Image Element src attribute.
The newly modified Image Element which results from our Javascript is shown here, and we can see that it differs from the originally generated HTML by the addition of the Source (src) and the Width and Height attributes. These newly appended attributes override the Source Set list. The dimensions resulting from our example source image present a width of 1118 pixels and a height of 1258 pixels. These dimensions are shown in the debug console figure above as the “Rendered size”, and as we stated previously our viewport is 1150 x 1258 pixels. Obviously the rendered image dimensions now fit wholly within the viewport at the maximum size possible while maintaining the original image aspect ratio.
Another final bit of interest is the CSS code we use to be sure our scaled image is centered horizontally within the viewport:
.imgblock img { position: relative; left: 50%; transform: translateX(-50%); }
Conclusion
Building on the foundations of design principles that embrace accessibility guidelines, we have extended our Builder application to improve the image download and display experience for our readers through a combination of adaptive and responsive design techniques.
Also published at .