Preloaded Resources
Every HTML page depends on resources, such as text files (html/js/css/etc.), images and fonts. Unsurprisingly, most of the time spent during every page initialization (and in some rare cases, its continued execution) goes to resource loading and preprocessing. Most resources require some type of preprocessing before they are ready to use - HTML/CSS/JavaScript require parsing, images require decoding, etc. Every resource needs to be read from the storage as well. Performing the aforementioned operations before your View starts initializing is referred to as resource preloading. Preloaded resources can give you a huge performance boost in page initialization. You can even achieve single frame initialization if you manage to preload all resources for a given page.
Most resource loading optimizations consist of finding a convenient time to load the resource in advance, for example during a “Loading” screen. There are two main ways to do that with Cohtml views:
- You can prompt Cohtml to preload and cache some types of resources to avoid processing them each time. For example, using the
PreloadAndCacheStylesheetAPI you can ensure that all of your CSS resources are already parsed and waiting when your page starts loading. Note that Font and CSS resources are cached system-wide, meaning that all Views which share the resource will benefit. - You can preload resources in the memory yourself, so you can skip fetching them from the storage when they are requested later on. For example, using a simple hash table you can cache the content of your JavaScript text files, ensuring that they are instantly available to Cohtml when requested.
Cohtml resource preloading APIs
Section titled “Cohtml resource preloading APIs”You can find example implementations of all APIs listed below in the Gameface Instaload sample.
Preloading fonts
Section titled “Preloading fonts”While standard CSS @font-face is recommended for general use, you can explicitly preload fonts via the System::RegisterFont API.
When executed successfully before a page loads, this API provides major performance and utility benefits:
- Support for Font Collections: It supports loading all fonts from a
.ttcor.otccollection at once, provided you do not supply a specific font description. If a description is provided, it behaves like@font-faceand only loads the first font in the collection. - First-Frame Readiness: By parsing the font description in advance, it ensures the font is ready to render on the very first frame without falling back to a default font, provided the preload completes before the page initializes.
- Eliminating IO operation spikes: By buffering the font entirely into memory, you can completely eliminate synchronous disk reads when new glyphs are rendered, preventing frame time spikes during gameplay.
This API does not guarantee that the font will be loaded by the time your page first initializes, as explained in Common pitfalls.
By default, fonts are streamed and loaded partially. However, upon registering a font, the part containing its description must be read immediately. Calling the cohtml::System::RegisterFont API will schedule a resource request for the font as a work job. When the work is executed, your OnResourceRequest callback will be invoked. Usually, you would respond to this request with an ISyncStreamReader implementation that reads the font from the storage. To completely preload the font and avoid IO reads at runtime, you can read and buffer the whole file inside your ISyncStreamReader implementation and later respond to read requests directly from that buffer. If you cannot spare the extra memory overhead, you can buffer the file only during initialization, release the buffer, and continue reading from the storage later.
Preloading CSS
Section titled “Preloading CSS”You can preload CSS files via the cohtml::System::PreloadAndCacheStylesheet API. Passing a CSS file to this API will trigger a corresponding OnResourceRequest callback to load the CSS file, parse it, then cache it inside Cohtml. Once loaded, the stylesheet will stay cached in Cohtml’s memory, making all future initialization of the same page or other pages that use it faster.
You can use RemoveStylesheetCacheEntry or ClearStylesheetCache to clear one or all pre-loaded stylesheets respectively after you are done using them.
This API does not guarantee that the CSS will be loaded by the time your page first initializes, as explained in Common pitfalls.
Preloading HTML
Section titled “Preloading HTML”You can preload HTML via the cohtml::System::PreloadAndCacheHTML API.
Passing a URL to this API will trigger a corresponding OnResourceRequest callback to load a valid HTML document, asynchronously parse it, and then cache it inside Cohtml. The parsed HTML will stay cached in Cohtml’s memory and will be used by all views if it’s requested.
The cache uses a URL as an identifier for preloaded HTML documents and ignores query parameters. When preloading a URL with query parameters, the HTML document from the response will be cached under a URL without the query parameters. When a URL with query parameters is requested from the cache, the cache will serve the cached HTML document that matches the URL without the query parameters.
RemoveHTMLCacheEntry and ClearHTMLCache can be used to clear cache entries from the HTML cache. Note that removing a HTML from a cache that is currently in use by a view won’t free the occupied memory immediately. It will be freed after all Views finish DOM building, even if the HTML is not loaded yet.
Most commonly preloaded HTML content will be used for accelerated loading of pages in a View using the View::LoadURL API. The HTML cache is also accessible via XmlHTTPRequest (XHR) in JavaScript and is effective for loading partial HTML content on the page. This can be achieved by sending an XHR with the preloaded URL and using its unmodified response for inserting content with DOM APIs such as innerHTML or insertAdjacentHTML. Modifying the response will invalidate the cache and will trigger HTML parsing.
This API does not guarantee that the HTML will be loaded by the time of your first usage, as explained in Common pitfalls.
Preloading JavaScript
Section titled “Preloading JavaScript”You can preload JavaScript by compiling it ahead of time and providing the result of the compilation when responding to the resource request. This significantly reduces the runtime overhead associated with initial JavaScript loading and enables faster UI load times. Responding to requests for JS resources with optimized data makes the first compile and run of said JavaScript roughly three times faster. The optimized data can easily be serialized and deserialized, allowing you to implement a disk cache for your JS files.
To achieve this, use the ScriptCompiler’s Compile API to generate an optimized data buffer corresponding to your .js files in advance. You can even do this before/during packaging. Later on, when a .js file is requested - use the optimized data buffer to respond to the request using ReceiveOptimizedData.
In C++, you handle the resource request by allocating space in the response for the file source and simultaneously passing the optimized data.
// Lets create a fileReader, which is a simple file read object, that can read a file with UTF-8 encoding and write it inside a source buffer.auto fileReader = CreateFileReader(filePath);
// We need to allocate space inside the response, as we usually do for all request-responses and then fill in the file source in that space.auto fileSrcLength = fileReader.GetSize();auto fileSrcBuff = response->GetSpace(fileSrcLength);fileReader.Read(0, static_cast<unsigned char*>(fileSrcBuff), fileSrcLength);
// Creating a script compiler can be slow, and therefore it is recommended to cache and reuse the script compiler for multiple compilations,// but in this snippet we will destroy it once we are done with the current compilation for the sake of simplicity.if (cohtml::ScriptCompiler* compiler = m_Library->CreateScriptCompiler()){ // To create the sourceData buffer, we use the UTF-8 encoded file source and length. auto sourceData = cohtml::DataBuffer::CreateDataBuffer( fileSrcBuff, fileSrcLength, [](unsigned char* data, void* userData) { // This is a destruction callback, it can be used // for handling custom deleter logic when needed. (void)data; (void)userData; }, nullptr);
// We need to specify the type of the script: ST_Classic or ST_Module. If wrong optimizedData is provided, it will be rejected by V8. // You can use a naming convention for modules or have an explicit list of script names that will be used as modules. // Alternatively you can try passing `ST_Classic` first and if the return value is `nullptr`, try with `ST_Module`. However this is not recommended // because scripts that will be used as modules but don't contain specific keywords like `import`/`export` might be successfully compiled as normal scripts. const cohtml::ScriptCompiler::ScriptType scriptType = GetScriptType(scriptName);
// The same compiler can be used for multiple compilations. auto optimizedData = compiler->Compile(sourceData, scriptType);
// The sourceData should be released as it is no longer needed. sourceData->Release();
if (optimizedData) { response->ReceiveOptimizedData(optimizedData); // The optimizedData can be released now or be cached and reused for future requests // for the same file from any View, and later released when it is no longer needed. optimizedData->Release(); }
compiler->Destroy();}
// Finally, we can finish the response.response->Finish(cohtml::IAsyncResourceResponse::Success);Preloading Images
Section titled “Preloading Images”Preloading image resources is a fairly complicated process, which rightfully deserves its own page.
Common pitfalls
Section titled “Common pitfalls”Due to Cohtml’s concurrent resource parsing, there is no way to guarantee that your resource will actually be parsed by the time the page starts loading. This is true for HTML, CSS, and Font preloading APIs. Consider the following snippet, which uses CSS as an example:
System->PreloadAndCacheStylesheet("SomeFolder/common.css"); // stylesheet will start pre-loading hereView->LoadURL("my_url.html"); // let's assume this page uses common.css// ...// later on, during your engine's frame executionView->Advance(time);In the example above, when your code reaches the View’s Advance call, Cohtml might still be loading common.css on another thread. In those types of cases, Cohtml will build your page without the resource, then update it on a later Advance call, after the resource is ready. In a similar way, a View won’t start building the DOM until the HTML is fully parsed.
If fonts, CSS, JS and HTML resources are not preloaded, they will be requested on page load and can slow down the FinishLoad event. That is why you should preload them first and as early as possible. One good practice, for example, would be preloading such files before doing any image or raw file preloading.
Preloading resources in the memory
Section titled “Preloading resources in the memory”When no API is provided for a given resource type, you can still preload the resource contents in the memory. A straightforward implementation of this can work as follows:
-
At some point in time before the HTML page is loaded, read the contents of the resource files from the disk and store them in memory. You can find an example implementation of this in the
ResourceHandler::AddPreloadedResource(path)function of the instaload sample.// Store file contents in memoryfor (auto& it : std::filesystem::recursive_directory_iterator(m_ResourcesRoot)){auto extension = it.path().extension();// JSON files are currently not preloadable by Cohtml, so they can be// preloaded in memory only.if (extension == ".json"){auto path = it.path().generic_string();// Assume ReadFile is a function that fetches the contents of a file in memoryauto contents = ReadFile(path);m_PreloadedResources.Emplace(path, contents);}} -
Modify your cohtml::IAsyncResourceHandler::OnResourceRequest implementation to check if the contents of the requested file are already loaded in memory. If they are, return them to Cohtml immediately, then signal that the request is finished.
void OnResourceRequest(const cohtml::IAsyncResourceRequest* request,cohtml::IAsyncResourceResponse* response)std::string path = GetPathFromRequest(request->GetURL());// Check if resource is preloadedauto findIt = m_PreloadedResources.find(request->path);if (findIt != m_PreloadedResources.end()){// Assume the function below passes the contents of the file// to Cohtml via the IAsyncResourceResponse API.PassFileContentsToCohtml(findIt-second, response);response->Finish(cohtml::IAsyncResourceResponse::Success);}else{// You can read the file from disk here and respond immediately,// or start some process that will eventually provide the file later on}
© 2026 Coherent Labs. All rights reserved.