Hosting local web assets
A CefBrowser control can serve HTML, JavaScript, CSS, and any other assets straight from a folder on disk — no embedded HTTP server required. Chromium’s SetVirtualHostNameToFolderMapping routes a virtual https:// hostname to a local folder so that resources behave as if they came from a real origin: same-origin fetch, Content Security Policy, service workers, and so on all work as expected.
This tutorial walks through the pattern used by Sample 1b — Chromium Embedded Framework Examples (forms Example 2, Example 3, Example 4).
The three-step pattern
- Choose a folder. It must exist on disk and contain
index.html(plus whatever assets the page wants — scripts, styles, images). - Register a virtual host mapping to that folder.
- Navigate to a URL under the virtual hostname.
Hook into the Ready event so the control is fully initialised before the mapping is installed:
Private Sub WebView_Ready() Handles WebView.Ready
Dim folderPath As String = _
Environ$("USERPROFILE") & "\Documents\MyApp"
WebView.SetVirtualHostNameToFolderMapping _
"myapp.example", folderPath & "\"
WebView.Navigate "https://myapp.example/index.html"
End Sub
Once mapped, every request to https://myapp.example/<path> is served from folderPath\<path>. A <script src="/script.js"> on the page resolves to folderPath\script.js exactly as if a real web server were sitting on myapp.example.
The trailing backslash on the folder path is required — the runtime concatenates the incoming URL path onto the folder string verbatim, so a missing separator turns folderPath + /index.html into a nonsense path.
Picking a hostname
The safe convention is to pick a hostname under a TLD that will never resolve on the public Internet:
| Recommended | Avoid |
|---|---|
myapp.example | myapp.com, app.local |
editor.invalid | editor.dev |
assets.test | assets.io |
The .example, .invalid, and .test TLDs are formally reserved by IANA and will never be allocated to a real domain, so they’re safe to use indefinitely.
Bundling assets in the project’s Resources folder
Most applications want to ship their HTML / JS / CSS inside the executable and drop them onto disk on first run. twinBASIC’s Resources folder is the right place to keep them.
- In the IDE’s Project explorer, expand Resources and add a sub-folder (right-click → Add new subfolder). Name it something memorable like
WEB_APP. - Drop the assets in —
index.html,script.js,styles.css, plus any sub-directories you need.
At runtime, the helper below copies the contents of a Resources sub-folder out to a local path. Drop it into a .twin module in your project:
Module Files
Private Sub CreateFile(ByVal Path As String, ByRef Data() As Byte)
On Error Resume Next : Kill Path : On Error GoTo 0
Dim fileNum As Integer = FreeFile
Open Path For Binary As fileNum
Put fileNum, 1, Data
Close fileNum
End Sub
Private Sub CreateLocalFileFromResource( _
ByVal OutputLocalFolderPath As String, _
ByVal InputResourceSubFolderName As String, _
ByVal ResourceName As String)
Dim splitPath As Variant = Split(ResourceName, "~")
On Error Resume Next : MkDir OutputLocalFolderPath : On Error GoTo 0
Dim i As Long
For i = 0 To UBound(splitPath) - 1
OutputLocalFolderPath &= "\" & splitPath(i)
On Error Resume Next : MkDir OutputLocalFolderPath : On Error GoTo 0
Next
Dim Data() As Byte
Data = LoadResData(ResourceName, InputResourceSubFolderName)
CreateFile(OutputLocalFolderPath & "\" & splitPath(i), Data)
End Sub
[Description("Copy every file from a Resources subfolder onto disk. " & _
"'~' characters in resource names represent subfolders.")]
Public Sub CopyResourcesFolderContentsToLocalPath( _
ByVal InputResourceSubFolderName As String, _
ByVal OutputLocalFolderPath As String)
Dim resourceId As Variant
For Each resourceId In LoadResIdList(InputResourceSubFolderName)
CreateLocalFileFromResource _
OutputLocalFolderPath, InputResourceSubFolderName, resourceId
Next
End Sub
End Module
LoadResIdList returns every resource ID under the named sub-folder; LoadResData hands back the bytes. The helper splits each resource name on ~ to reconstruct the original sub-directory tree on disk — the twinBASIC IDE flattens nested folders by joining their names with ~ when the resources are compiled in.
Putting it together
The complete deploy-on-Ready pattern looks like this:
Private Sub WebView_Ready() Handles WebView.Ready
' Resources/WEB_APP/* is copied here on every launch.
Dim folderPath As String = _
Environ$("USERPROFILE") & "\Documents\MyApp"
CopyResourcesFolderContentsToLocalPath "WEB_APP", folderPath
WebView.SetVirtualHostNameToFolderMapping _
"myapp.example", folderPath & "\"
WebView.Navigate "https://myapp.example/index.html"
End Sub
Once deployed, the application can launch DevTools (OpenDevToolsWindow) to inspect the loaded files, and users can edit index.html directly on disk and hit Refresh — useful for rapid iteration during development.
Removing a mapping
ClearVirtualHostNameToFolderMapping removes a mapping previously installed by SetVirtualHostNameToFolderMapping:
WebView.ClearVirtualHostNameToFolderMapping "myapp.example"
The browser keeps cached assets until a hard reload, so a navigation that hits the just-removed hostname may still succeed for a short while.
Where next
- JavaScript interop — how a hosted page exchanges values and method calls with the BASIC application.
- Driving Monaco from twinBASIC — a full case study built on top of this pattern.
- SetVirtualHostNameToFolderMapping — full reference.