Flickr photo plane


This prototype approach uses dynamic .fbx textures to display something like a Flickr slide show.

A double-sided model is used to prevent the image plane from becoming “invisible” when viewed from behind and entity dimensions are updated responsively to accommodate different aspect ratios.

To try it out just add a new model entity with modelURL:

And then set its scriptURL to:

(once loaded clicking on the plane will move to the next image in the set)

The default feed pulls from a “best of” flickr group, but you can append hash options (to the scriptURL) to filter by tags, user or a different group id:

  • by tag(s): #tags=water,droplets
  • for a particular flickr user: #@id=012345@N00
  • for a particular flickr group: #@group=012345@N00

( can be used to resolve a flickr user or group name into a flickr id)

Here is an example scriptURL that would display kittens and fit images to entity height instead of width:,kittens&fit=height

Other notes:

  • right now to refresh the image set the userData property has to be cleared manually (and then scriptURL updated or reloaded)
  • flickr’s public feeds return up of 20 images, but HiFi has a bug/feature where userData larger than a threshold never gets propagated; to avoid hitting that, only the first 5 image urls are put into rotation
  • if you make each .fbx URL unique (eg: by adding #left-wall-poster-1 to modelURLs), several image planes can be used at the same time to display different images or even different feeds:


… some rough brain dumps and follow-up observations…

Hosting Costs:

  • the assets on my side are static (so out-of-pocket hosting approximates bandwidth)
  • each serving of the .fbx and .js consumes ~8.5k of data transfer (which includes headers)
  • estimated hosting cost: one penny per 60,000 servings

Hash Fragments:

Using hash fragments like #v=1 seems preferable for static assets over querystrings like ?v=1:

  • querystrings tend to create virtual resources as a side-effect
    • for a 10MB asset ?v=1 through ?v=5 travels the net as 5 resources x 10MB each
  • hash fragments are purely client-side state
  • #v=1 through #v=5 still all refer to the same one resource

Hash fragments also offer a privacy improvement:

  • hash fragments are not sent to the server (so neither is your flickr configuration (to me))
  • Apache logs the resource and IP address, but that merely suggests that someone (some IP) somewhere (??) did something (??)
  • which user(s), domain(s), flickr settings or images remains a mystery

HTTP Cache Negotiation:

  • Interface and Apache appear to be successfully negotiating!
  • To upgrade assets I’d just upload new files. (changes then propagate organically, consistently)
  • Hash fragments made testing cache behavior way easier and in part because the logs become more obvious (there are no virtual permutations to deal with)
  • Interface uses different validation mechanisms for different resource types
    • typically HEAD for models and GET/304s for scripts
  • Currently a different loophole is used to trigger cache conversations manually:
  • on the surface: #v=1 --> #v=2
  • under the hood:
    • Interface perceives URLs w/different hash fragments as completely(?) different
    • BUT the disk cache and networking ignore the hash (as they should)
    • So varying the hash effectively just “reminds” Interface that it is time to phone Apache
    • Which results in the negotiation: IF newer asset THEN download ELSE hang up.


Flickr serves the images themselves from a massively-parallel server farm (that already handles an incredible amount of simultaneous users and traffic). The bootstrapping assets are featherweight. The GPU and CPU overheads are basically nominal.

So… this thing is insanely scalable. Hundreds of users? Think bigger. (I am not suggesting abusing Flickr here; one day Flickr will form an opinion regarding such metaverse use cases; today it would take a substantial effort to become a blip on their radar at the resource level, or even to frame the discussion at all).

Side note: Web Entities are great (and could browse to Flickr directly), but they actually “cost” nearly as much as a browser tab – which is very, very expensive (in relative terms). A blank page already costs hundreds if not thousands of times more in general than eight vertices, four triangles and a bitmap. And this is per each instance of a Web Entity. There are solutions to that bottleneck but not yet convenient ones.

Duct Tape

The photo plane would probably need to be taught a few more tricks (like best-fit autosizing to stay within bounds, specifying a starter image from a set, taking a flickr API key as a parameter, etc.). But then it would work almost like “metaverse duct tape.” :wink:

Because pretty much anything that is flat, rectangular and photographed becomes emulate-able:

  • temporary traffic signage for your highway (#tags=highway+exit+signs ?)
  • a picnic blanket (#tags=quilt+pattern ?)
  • dynamic wallpaper – like for your walls (or ceilings (or carpets (or cabinets)))
  • a placeholder crop of soybeans in the distance
  • exterior graffiti


Sorry to bump such an old post but this is a good one.
@humbletim I love your posts, and I love this flickr script and have been using it since you posted it.

It has worked fine until recently, not sure exactly when but some time in the last few weeks it stopped…

The script loads and runs as normal, the new edit window userData display now displays the json in a nice format and still shows all the 5 images it has loaded.
It displays the first image then doesnt change on click, just keeps the first image.

Here is the log from loading

[09/07 16:27:37] [DEBUG] Found script in cache: “,kittens&fit=height
[09/07 16:27:37] [DEBUG] flickrtest.js version 201507144453
[09/07 16:27:37] [DEBUG] script:print()<< flickrtest.js version 201507144453
[09/07 16:27:37] [DEBUG] script:print()<< flickrtest: preload {7adb7ac5-699c-4095-af21-975cbfa1d1c2}
[09/07 16:27:37] [DEBUG] script:print()<< flickrtest: no current image list, fetching from flickr… old length: undefined
[09/07 16:27:37] [DEBUG] script:print()<< @args: {“tags”:“cute,kittens”,“fit”:“height”}
[09/07 16:27:37] [DEBUG] script:print()<< FETCHING FLICKR FEED:,kittens&format=json&nojsoncallback=1
[09/07 16:27:38] [DEBUG] script:print()<< xhr.status=200
[09/07 16:27:38] [DEBUG] script:print()<< GOT 20 FLICKR ITEMS!
[09/07 16:27:38] [DEBUG] script:print()<< … limiting images to first 5 (to avoid MTU userData overflow)
[09/07 16:27:38] [DEBUG] script:print()<< data.images.length is 5
[09/07 16:27:38] [DEBUG] script:print()<< goto(undefined) – data.images.length is 5
[09/07 16:27:38] [DEBUG] script:print()<< goto(undefined) – after lock, data.images.length is 5
[09/07 16:27:38] [DEBUG] script:print()<< current flickr image index: 0
[09/07 16:27:38] [DEBUG] script:print()<< textures.img ‘undefined’ -> ‘
[09/07 16:27:38] [DEBUG] script:print()<< updating aspect ratio dimensions: {“x”:1.4999999105930328,“y”:0.9999999403953552,“z”:0.00001}
[09/07 16:27:38] [DEBUG] script:print()<< dimensions now: {“x”:1.4999999105930328,“y”:0.9999999403953552,“z”:0.00001}

I havent taken much notice of the log before because it just worked, I dont know if the goto(undefined) means anything wrong or just a fresh load with no previous data.
I put in a debug to test the click and the this.clickDownOnEntity function seems to be working ok, but not affecting the image.
Would you have any suggestions what I might try please?


That’s cool to hear! :slight_smile: (didn’t know anybody was actively using it)

Ahh… looks like the undocumented MouseEvent? (of clickDownOnEntity etc.) changed formats slightly.

I’ve updated my script to accommodate – could you try it out and confirm that clicks work again?

Note that bumping .scriptTimestamp didn’t seem to work for making my test Entity pull the updated script – instead I had to introduce a stronger ‘?’ presuffix (eg: flickr.min.js?#tags=cute,kittens&fit=height). In general the script reports its build stamp to the debug log – so when using the latest version should see >= “flickrtest.js version 201609133403.”


Thank you for a really quick response.

Yes I can confirm it works perfectly.