Pannellum 2.2

After a little over a year of work, I’ve just released Pannellum 2.2.0. New features in this release include an experimental API, instead of just a standalone viewer, for better integration with external code; support for the PosePitchDegrees and PoseRollDegrees XMP tags, as used by the Ricoh Theta S; an optional fade animation for tour transitions; and a debug parameter to assist with placing hot spots. Noteworthy improvements include vastly improved equirectangular video support, high-DPI display support, and unification of regular and tour configuration files. Finally, there are numerous bug-fixes.

Previously, equirectangular videos were sort of supported, but the user experience was terrible, as there were no playback controls. Pannellum is a panorama viewer of course, not a video player, so I didn’t want to write my own video player nor did I want to build it into Pannellum—this is where the new API comes in. Instead, I wrote a Video.js plug-in that uses Pannellum to render the video; this achieves the objective of a fully-featured equirectangular video player without actually having to write a video player.

I would have created a new release sooner, but I didn’t want to release yet another version without much in the way of documentation, specifically examples. At the beginning of December, I received the Ricoh Theta S I had pre-ordered, allowing me to quickly shoot a variety of panoramas1 to use with examples—this is when my push to write documentation started in earnest. To this end, I converted pannellum.org from using Jekyll to using Hugo, as it better suited the documentation effort (and moved it to a dedicated repository). I then wrote examples, integrated the existing configuration parameter documentation, and added the new API documentation. Finally, I created a Pannellum CDN and integrated a configuration generator to allow one to easily display CORS-hosted panoramas (e.g. hosted with Imgur).

As always, report bugs on the GitHub issue tracker.


  1. Actually, they’re all of Baltimore near my apartment or of the area around San Pedro de Atacama, Chile where I’m currently working.  

Posted in | Tagged , , , | 10 Comments

Climbing Lascar

Two weeks ago, I climbed to the summit of the Lascar volcano in northern Chile. The 5592 m tall active volcano last erupted in October and is the most active volcano in northern Chile. Before climbing the volcano, one first has to find it. While finding the volcano itself on a map is pretty easy, information on the location of the trailhead and how to get to it proved more illusive. Most descriptions I found online contained only loose wording and none included any sort of map, which is important due to the lack of road signs in the area. I eventually found a KML file on a shady download site that contained the trail and the track to the trailhead, which was mostly correct.1 This provided enough information to find the trail, in combination with a GPS receiver.

“Road” to Lascar Continue reading


  1. The parking area was shown in an incorrect location.  

Posted in | Tagged , , , , | 6 Comments

Bandwidth-efficient HDR WebGL Textures

Storing high dynamic range (HDR) images is a solved problem in general, such as using the excellent OpenEXR format. When is comes to the web, however, there are two major problems. The first is that web browsers don’t support any of the standard HDR image formats, and the second is that HDR images tend to be rather large, which is a problem for conserving bandwidth and reducing load times. Although there are ways to load HDR textures into WebGL using pre-encoded textures and WebGL extensions, this is very bandwidth-inefficient and not supported by all WebGL devices. Thus, the first problem has to be worked around by encoding the HDR image information into standard web images, e.g. JPEG and PNG. The earliest example of this that I can find is the pre-WebGL pfstools HDR HTML viewer, which layers multiple JPEG images at different exposures and blends them with different opacity settings.1 This method was good for its time, since before WebGL image processing calculations were difficult to perform in the browser. However, the method is bandwidth-inefficient as it requires downloading the same image at multiple exposures—five images in the example. This brings us to WebGL, where a method PNG-based method was developed by SpiderGL (and copied by three.js). Although the provided encoder is undocumented, it appears to be based on JPEG-HDR, which uses a tone-mapped base image combined with a subband image that encodes the ratio between the tone-mapped image and the original HDR image.2 The method encodes the tone-mapped image in the RGB channels of a PNG and the ratio in the alpha channel. This works well when lossless compression is desired but is bandwidth-inefficient for photographic textures.

Continue reading


  1. Rafał Mantiuk and Wolfgang Heidrich. “Visualizing high dynamic range images in a web browser”. In: Journal of Graphics, GPU, and Game Tools 14.1 (2009), pp. 43–53. DOI: 10.1080/2151237X.2009.10129276. URL: http://pfstools.sourceforge.net/papers/mantiuk09vhdri.pdf.  

  2. Greg Ward and Maryann Simmons. “JPEG-HDR: A backwards-compatible, high dynamic range extension to JPEG”. In: ACM SIGGRAPH 2006 Courses. ACM, 2006, p. 3. DOI: 10.1145/1185657.1185685. URL: http://www.anyhere.com/gward/papers/cic05.pdf.  

Posted in | Tagged , , , | 12 Comments

MacBook Webcam Teardown

With the release of the mid-2013 MacBook models, Apple stopped using the standard USB video device class interface for the laptops’ webcams. On these laptops, the webcam connects via the PCIe bus, enumerating as a Broadcom 1570 device. Unfortunately, this interface is completely undocumented, and there is no Linux driver, although an effort is being made to reverse engineer one. Since I thought understanding the device hardware might aid this driver development effort, I bought a replacement webcam to disassemble. The front of the camera, from left to right, contains a 12-pin connector; power management circuitry, consisting of some sort of crystal or resonator, a Texas Instruments TPS657091 power management IC, and assorted passives; an ambient light sensor (with decoupling capacitor); the image sensor and fixed-focus optics; and an LED. The back of the device contains identification markings, which include a DataMatrix barcode and text, both of which read CC2 3222 2UYE F9T9CF on my particular camera.

Front of Camera
Back of Camera Continue reading

Posted in | Tagged , , , , , , , | 2 Comments

An Efficient Method of Serving Many Small Files

Certain types of web content, such as map tiles and image pyramids1, require serving many small files, but this can often be inconvenient. Transferring a large number of small files over SCP or SFTP is very slow, and managing these files on disk can also be unpleasant. The transfer problem can be solved by adding the files to a tarball before transferring them, transferring the tarball, and untarring them once transfered. However, this is an extra step, and it doesn’t address the file management problem. Mapbox devised a solution for this problem for map tiles with their MBTiles format. This container format stores map tiles in a SQLite database, and a server implementation is then used to serve tiles directly from the container. This cleanly solves the aforementioned transfer and storage problems, but it is not general purpose and only works for map tiles. This specialization allows for additional optimizations such as deduplicating identical map tiles, but it means it can’t be used for storing image pyramids or other uses.

The use of a SQLite database container format can also work as a general purpose solution, provided a general purpose database key is used. Thus, I propose a general purpose “FilesDB” format as a generic solution. This format consists of an SQLite database containing a files table, which in turn contains a filename column of type text and a data column of type blob. A directory of files is stored in the format by storing each files in the directory in the database with the file’s path relative to the the base directory2 as the filename and the file’s contents as the data. As a proof of concept, I wrote a Python script for generating a .filesdb container and a rudimentary server for serving the files from the format in Go. These, along with a basic specification document, are available from a repository on GitHub.


  1. Used by Pannellum’s multires format, for example. 

  2. Using Unix directory separators (/

Posted in | Tagged , , , | 1 Comment