Experiment – Creating A Single-File Tumult Hype Project 🧪

One of the nice things about Flash – it was a single .swf file. With Hype, you have CSS, HTML, JavaScript and Images scattered about. That's just the nature of an HTML5 project. It's a lot of separate downloads. Making so many simultaneous connections might be bad for the server. It could also hurt performance.

Meanwhile, I've been working on Widgets. I've been investigating the possibility of adding custom widgets to the app. In particular, since I like the software and this community, the possibility of those custom widgets being created by Hype. The challenge is that I can't use a sandboxed iFrame with local resources. To circumvent this issue, I've been exploring the possibility of making everything self-contained.

Tonight, I saw this...

GitHub - remy/inliner: Node utility to inline images, CSS and JavaScript for a web page - useful for mobile sites

Inliner – Turns your web page to a single HTML file with everything inlined - perfect for appcache manifests on mobile devices that you want to reduce those http requests.

It's getting late, so I haven't gotten to test it yet, but it looks interesting. I noticed that a lot people posted about the "Single-File" problem before…

Save to a single file
Inline Javascript and CSS Export? - #4 by teo1310 (Looks like Inliner was mentioned, but not linked.)
Does Hype generate BAD code? (I don't think Hype generates bad code, and that sentiment was echoed in that thread, but perhaps inlining can have performance advantages.)

I'm planning to look at the potential of Hype and Inliner.

The hypothesis is that perhaps Inliner can fix the sandboxing custom widgets problem, while making Hype more powerful too. Since the software seems to be script friendly, Export Scripts might make short work out of this challenge.

But for now, the plan is to eat some snacks, watch some YouTube, maybe play some old Sega Genesis games. (HA HA. I got tired.)


Hi Michael!

I’m not familiar with creating a project such as You are, nor am I terribly familiar with the iFrame sandboxing.

So I’ve been reading through various posts on the internet and I see that the iFrame can “allow-same-origin” content.

What am I not understanding about this iFrame attribute as it applies to your project… does being an app change things?

Thank You.

1 Like

The idea is to have the app work mostly offline. Sandboxing iFrames can work when the iFrame is hosted on a website. But if it’s loaded locally, sandboxing prevents the loading of additional local files in the iFrame. Allow-Same-Origin, with allowing JavaScript, negates the sandboxing.

<iframe>: The Inline Frame element - HTML: HyperText Markup Language | MDN

When the embedded document has the same origin as the embedding page, it is strongly discouraged to use both allow-scripts and allow-same-origin , as that lets the embedded document remove the sandbox attribute — making it no more secure than not using the sandbox attribute at all.

It is an issue that is somewhat rare. That’s because, in my project, the HTML is loaded locally and the iFrame also has local content. At least, that’s how I’m trying to solve the problem of customization.

Thank You for the explanation. :pray:

1 Like

So, I played with Inliner and here are my notes...


To use Inliner, node.js is required. While I've used that software before, I didn't have it on my Mac Mini. I was concerned about being able to uninstall/upgrade node.js if necessary, so I used Brew.

https://coolestguidesontheplanet.com/installing-node-js-on-macos/ (I like this site, but I ended up using Brew to install the software.)

There's something I don't understand about modern web development. Computers are getting more powerful, but I find myself using the Terminal app a lot more. But in general, the commands I used weren't too bad. Basically, it went like this...

  • Install Brew
  • Turn off Brew analytics :crazy_face:
  • Install Node
  • Install inliner
  • Read the inliner "help" text
  • inliner /Users/Photics/Desktop/html/index.html > /Users/Photics/Desktop/compressed/index.html

The First Experiment

For my first experiment, I used Widgets. Heh, not surprisingly, it didn't work. The generated page wouldn't load the JavaScript properly. That's because the order is very sensitive in my project. Event listeners can't be added to elements that don't exist yet. I moved my JavaScript to the bottom of the HTML.

It (mostly) worked...


I'm impressed by that number. It takes less than half a second to process the project, but the end result can save so much time for so many people. This is part of high-end Search Engine Optimization, where fussing over milliseconds matters. That's because page rendering and page load speed matters!

While not highly scientific, I used the Safari Web Inspector to display the differences in page load speed. These numbers can vary significantly. The low numbers – without compression was 48.9ms, with compression 27.1ms.

That might not seem like much, but I've already been focusing on optimization. To see numbers that low was surprising to me.

Even with that fix, there still was an issue with the project. Maybe I'm missing something, but I wasn't able to get "Inliner" to work with dynamically added images. Example, the weather icons were not added to the single HTML file. I suppose I could fix that problem too, but I'm intentionally leaving the code uncompressed.

The Hype Experiment

I was more curious about how this would work with Hype.

TL;DR – It didn't
(At least, it didn't work for me.)

I used "Circles with Grandma" as an example. It has a nice mix of JavaScript files and images. There was a problem inlining both of those file types. Here's a snippet from the output log...

20% remaining: js(1), styles(2), style-attrs(1)
20% remaining: js(1), styles(2), style-attrs(1)
40% remaining: js(1), styles(1), style-attrs(1)
60% remaining: js(1), style-attrs(1)
80% remaining: js(1)
‣ no such file: circles_hype_generated_script.js?69457

80% remaining: js(1)
80% remaining: js(1)
Last job: compressing javascript
Time: 0s 76.380ms

Ironically, I was able to fix that problem by exporting the Hype project with the "Inline data file+loader" Advanced Export option. After that, there was a new problem...

Not allowed to load local resource: file:///Users/Photics/Desktop/compressed/circles.hyperesources/HYPE-654.full.min.js

The Hype Runtime file wasn't embedded. So, I just cut-and-paste it in compressed file. That solved that problem... but then there was a new problem. The images weren't loading properly.

At this point, I realized it was a bridge too far for most users. They'd have to install a bunch of command-line based software, only to get unexpected compression results.


I don't think this is a solution for my custom widget problem. I do think that this is a step in the right direction though. Having a single-file Hype project was nice. Ah, that reminded me of the heydays of Flash.

This part may have been the real problem.
It’s not really part of the file path.

Also I was wondering if there was some way of implementing the OAM files?

You could do some of the inlining in an exportscript. But that would require writing it nearly from scratch. I guess you will rather want to focus on your main project then getting derailed with a export script :slight_smile:

Very astute! True scientific method here!

While I was testing, I was thinking that was probably nothing, but that was just an assumption. Maybe it was the problem. Can "Inliner" handle query strings in hyperlinks / URLs?

I'm still not sure, but removing the randomizer didn't help with getting the Hype project to work. Inlining the project – before using "Inliner" seems the better way to go.

This is also a good comment. So, I looked into finding out... what exactly is an OAM file? Apparently, it's just a .zip file. It's certainly not plain text.

Well, I don't want to. It's a lot of work. But, the "Custom" widgets for "Widgets" is probably the flagship widget – even more important than the weather widget.

I suspect Tumult doesn't want to do this either. At least, it's not a feature that's high on their priority list. They'd have to add Node and Inliner to the Hype app, if that's the method they want to use, and then they'd have to modify their exporting to better send the data to Inliner.

So, if I wanted to create the export script... maybe even as an open source project ...what steps would I need?

  • Pick the technology – Hype is macOS. That doesn't look like it will change soon either. So, JXA looks OK. I could leverage JavaScript, which is ideal for a project like this.
  • base64 the images – That's why I picked JXA. That's trivial. It's a simple JavaScript command... btoa() ...Although, this can be done easily with Shell too.
  • Modify the HTML – This part sucks. I'm not sure how to do that. It might be easier to generate a new index.html file, rather than creating a new one.
  • merge the CSS – Easy?
  • merge the JavaScript – It's not too bad to "uglify" the JavaScript and then add it to the HTML. The problem is fixing any references to file paths.

That last part is the problem, and why I think Inliner has trouble with Hype projects. Hype doesn't just list all of the resources in the index.html file. It's in the JavaScript.

So yeah, the short term plan is to focus on other widgets.

I suppose the custom Widget would work as I imagined – as long as the HTML is inlined. That means no Hype collaboration... but I like Hype. :slightly_smiling_face:

It used to export to Dashboard. What's Hype going to do in a "Catalina" 10.15 world? Eh, again – this is probably not a priority for Tumult. They can just drop the word "Dashboard". The export feature is still good for iBooks. :smile:

The exports script would need to be Python.

Again, Python does this also pretty well.

In the sample export script there are demos for nearly all callbacks. One demonstrates modifiying the HTML. See --modify_staging_path

Also possible in the packing process, also found in the example under --modify_staging_path

Hype already has the option to inline itself in the export script see --get_options. The actual runtime would need to be added in the --modify_staging_path run.

This code should fix your problem

It uses the HypeResourceLoad event and reads the files from variables instead like regular from the ressource library. You could use this technic (without the encoding to the console portion) if you put it into the export script.

You can use JXA, Shell too.

Export Scripts can be shell, python, or ruby scripts.

Heh, I posted in that thread too... but I don’t even remember it.

Looks like I should look more into the export script options.

I don’t see how inliner would work with code that is heavily dynamically driven like Hype’s output unless it made a lot of assumptions and dangerous replacements.

I think an export script like what @MaxZieb outlines is definitely the way to go. It can write some JS at the top that keys filenames to a base64 encoding, and then uses that in the HypeResourceLoad callback. It won’t tackle all cases where non Hype runtime-loaded assets are used (js, fonts, html widgets/iframes, etc.), but should get most of the way for documents that just use image assets.

1 Like

Flash does require more than a single file - images, video, etc.