Embed modern web content directly into your Rainmeter skins with full JavaScript interactivity
🚀 Modern Web Engine
Powered by Microsoft Edge WebView2, supporting:
- ✅ HTML5, CSS3, JavaScript ES6+
- ✅ Modern frameworks (React, Vue, Svelte)
- ✅ WebGL, Canvas, SVG animations
- ✅ Transparent backgrounds by default
🔌 Seamless JavaScript Bridge
Two-way communication between web and Rainmeter:
- ✅ Call Rainmeter API from JavaScript
- ✅ Execute JavaScript from Rainmeter
- ✅ Real-time data synchronization
- ✅ Custom events and callbacks
⚡ Dynamic & Flexible
- ✅ Load local HTML or remote URLs
- ✅ Multiple WebView instances per skin
- ✅ Hot-reload without flickering
- ✅ Developer tools (F12) built-in
Before you begin, make sure you have:
| Requirement | Version | Status |
|---|---|---|
| Windows | 10 (1803+) or 11 | |
| Rainmeter | 4.5 or higher | |
| WebView2 Runtime | Latest |
📦 Don't have WebView2 Runtime?
Good news! Windows 11 includes it by default. For Windows 10:
- 🔗 Download WebView2 Runtime
- 🎯 Choose "Evergreen Standalone Installer"
- ⚡ Run the installer (takes ~1 minute)
The easiest way to get started!
- 📦 Download the .rmskin file
- 🖱️ Double-click to install
- ✨ Done! Plugin and examples are ready to use
Rainmeter will automatically install everything you need
Click to expand manual installation steps
-
Download the plugin DLLs from Releases
-
Choose the right version:
📁 x64/WebView2.dll ← For 64-bit Rainmeter (most common) 📁 x32/WebView2.dll ← For 32-bit Rainmeter -
Copy to your Rainmeter plugins folder:
%AppData%\Rainmeter\Plugins\ -
Restart Rainmeter
Create a new skin with this minimal configuration:
skin.ini
[Rainmeter]
Update=1000
[WebView2]
Measure=Plugin
Plugin=WebView2
URL=file:///#@#index.html
W=800
H=600
X=0
Y=0
[Background]
Meter=Image
W=800
H=600
x=0
Y=0
SolidColor=0,0,0,1Create index.html in your @Resources folder:
index.html
<!DOCTYPE html>
<html>
<head>
<style>
body {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
font-family: 'Segoe UI', sans-serif;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
width: 100vw;
margin: 0;
}
h1, p { animation: fadeIn 1s; }
h1 { font-size: 2.8em; user-select: none;}
p { font-size: 1.3em;}
p + p { margin-top: 0.1em; }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
</style>
</head>
<body>
<h1>🎉 Hello Rainmeter!</h1>
<p>Hold CTRL to drag the skin</p>
<p>CTRL + Right Click to open the Skin Menu</p>
</body>
</html>That's it! Load the skin and see your first WebView in action.
👑 Measure Values
WebView's measure returns the following values:
Number Value
The number value represents the internal state of the WebView2 instance while it's initializing. When the WebView2 instance is initialized, the number value represent's the navigation state.
-2WebView failed during initialization.-1WebView2 is idle.0WebView2 is initializing.1WebView2 is initialized.100Navigation has started.200Web content is loading.300Web is loaded.400Navigation has finished.
Whenever the state changes, the plugin triggers OnStateChangeAction.
String Value
The string value represents the current URL. This URL will change when the user navigates through WebView either internally by clicking on links or externally by using commands.
CurrentURL
Whenever the URL changes, the plugin triggers OnURLChangeAction.
🛠️ Measure Options
| Option | Description | Default | Examples |
|---|---|---|---|
| WebView Options | |||
AutoStart |
Automatically load WebView on skin load.0 = Disabled,
1 = Enabled
|
1 |
AutoStart=0 |
URL |
The URL WebView will navigate to when it starts. This is the URL the Navigate Home command navigates to.Supports: file paths and web URLs Supported schemes: file:///, http://, https://, view-source:Relative paths are also supported: URL=file.htmlPaths are relative to #CURRENTPATH#.When HostPath is set to a valid folder path, this option is relative to that folder and will navigate to the default virtual host: https://rootconfig/file.htmlSee Setting Up a Virtual Host. |
Required |
URL=file:///#@#file.htmlURL=http://example.comURL=path\to\file.htmlURL=path\to\img.gifURL=file.html
|
| Virtual Host Options | |||
HostSecurity |
Set a preferred protocol for the virtual host:0 = http (not secure), 1 = https (secure)Https allows to use JavaScript APIs that are normally blocked by CORS. See Setting Up a Virtual Host. |
1 |
HostSecurity=0 |
HostOrigin |
Set a preferred host name for the virtual host:0 = current-config (not shared), 1 = rootconfig (shared)When current-config 0 is used, the local storage is isolated to the current skin's origin, eg. https://illustro-clockWhen rootconfig 1 is used, the local storage is shared between all configs that are part of the same suite, eg. https://illustroSee Setting Up a Virtual Host. |
1 |
HostOrigin=0 |
HostPath |
The path to the folder that contains the index.html that the virtual host will load.When this option is set to a folder path, the URL option will become relative to the path set on this option.See Setting Up a Virtual Host. |
"" |
HostPath=path\to\folderHostPath=#CURRENTPATH#HostPath=#@#
|
| Window Options | |||
W |
Window width (pixels) | 800 |
W=1920 |
H |
Window height (pixels) | 600 |
H=1080 |
X |
Horizontal position offset | 0 |
X=100 |
Y |
Vertical position offset | 0 |
Y=50 |
ZoomFactor |
Site zoom factor | 1.0 |
ZoomFactor=1.5 |
Hidden |
Window visibility0 = Visible,
1 = Hidden
|
0 |
Hidden=1 |
Clickthrough |
Mouse interaction mode0 = Interactive,
1 = Click Through,
2 = Press CTRL to toggleThis option is very usefull to drag the skin, while set to 2 press CTRL to drag the skin and RMB to open the Skin Menu.
|
2 |
Clickthrough=1 |
| Browser Options | |||
UserAgent |
Custom user-agent string override | "" |
UserAgent=MyBrowser/1.0 |
ZoomControl |
Allow user-controlled zoom through:CTRL+SCROLL, CTRL + PLUS, CTRL + MINUS and CTRL + 00 = Disabled,
1 = Enabled
|
1 |
ZoomControl=0 |
NewWindow |
Allow opening links in a new window0 = Disabled,
1 = Enabled
|
0 |
NewWindow=1 |
Notifications |
Override JavaScript's Notification API permission.0 = Deny,
1 = Allow
|
0 |
Notifications=1 |
AssistiveFeatures |
Allow Print, Find and Caret Browsing features.0 = Disabled,
1 = Enabled
|
1 |
AssistiveFeatures=0 |
💡 Pro Tip: When
DynamicVariables=1, the WebView updates smartly:
- URL changes → Sets a new Home Page
- Size/Position changes → Applied instantly, no flicker
- Visibility changes → Instant toggle
- All options can be updated dynamically
▶️ Measure Actions
| Action | Description | Example |
|---|---|---|
| WebView State Actions | ||
OnWebViewLoadAction |
Triggers when WebView starts. | OnWebViewLoadActio=[!log "WebView2 loaded succesfully!"] |
OnWebViewFailAction |
Triggers when WebView fails. | OnWebViewFailAction=[!log "WebView2 failed :("] |
OnWebViewStopAction |
Triggers when WebView stops. | OnWebViewStopAction=[!log "WebView2 has stopped!"] |
OnStateChangeAction |
Triggers when WebView initialization or navigation states change. | OnStateChangeAction=[!UpdateMeasure #CURRENTSECTION#][!UpdateMeter CurrentState][!Redraw] |
| Navigation Actions | ||
OnUrlChangeAction |
Triggers when the current URL changes. | OnUrlChangeAction=[!UpdateMeasure #CURRENTSECTION#][!UpdateMeter CurrentURL][!Redraw] |
OnPageLoadStartAction |
Triggers when navigation starts. | OnPageLoadStartAction=[!log "Navigation has started!"] |
OnPageLoadingAction |
Triggers when the page starts loading. | OnPageLoadingAction=[!log "Page is loading!"] |
OnPageDOMLoadAction |
Triggers when the DOM content is loaded. | OnPageDOMLoadAction=[!log "DOM content loaded!"] |
OnPageFirstLoadAction |
Triggers the first time a page is loaded. | OnPageFirstLoadAction=[!log "First time on this page!"] |
OnPageReloadAction |
Triggers when the page is reloaded. | OnPageReloadAction=[!log "Page has been reloaded!"] |
OnPageLoadFinishAction |
Triggers when the navigation is finished. | OnPageLoadFinishAction=[!log "Navigation has finished!"] |
💥 Bang Commands
Control your WebView with Rainmeter bangs:
| Command | Description | Example |
|---|---|---|
| WebView | ||
WebView Start |
Starts the WebView instance. | [!CommandMeasure WebView2 "WebView Start"] |
WebView Stop |
Stops the WebView instance. | [!CommandMeasure WebView2 "WebView Stop"] |
WebView Restart |
Restarts the WebView instance. | [!CommandMeasure WebView2 "WebView Restart"] |
| Navigate | ||
Navigate Home |
Navigates to URL option. |
[!CommandMeasure WebView2 "Navigate Home"] |
Navigate Back |
Navigates to the previous page. | [!CommandMeasure WebView2 "Navigate Back"] |
Navigate Forward |
Navigates to the next page. | [!CommandMeasure WebView2 "Navigate Forward"] |
Navigate Reload |
Reloads the current page. | [!CommandMeasure WebView2 "Navigate Reload"] |
Navigate Stop |
Stops any navigation. | [!CommandMeasure WebView2 "Navigate Stop"] |
Navigate URL |
Navigates to a URL. | [!CommandMeasure WebView2 "Navigate http://example.com"] |
| Open | ||
Open DevTools |
Opens DeveloperTools. | [!CommandMeasure WebView2 "Open DevTools"] |
Open TaskManager |
Opens the web task manager. | [!CommandMeasure WebView2 "Open TaskManager"] |
| Execute | ||
Execute ScriptExecute File.js |
Executes given JS script or .js script file |
[!CommandMeasure WebView2 "Execute alert('Hello Rainmeter!')"] [!CommandMeasure WebView2 "Execute #@#script.js"]
|
🗺️ Defaults Map
[WebView2]
Measure=Plugin
Plugin=WebView2
; WebView Options
AutoStart=1
URL=""
; Virtual Host Options
HostSecurity=1
HostOrigin=1
HostPath=""
; Window Options
W=800
H=600
X=0
Y=0
ZoomFactor=1.0
Hidden=0
Clickthrough=2
;Browser Options
UserAgent=""
ZoomControl=1
NewWindow=0
Notifications=0
AssistiveFeatures=1
; WebView State Actions
OnWebViewLoadAction=[]
OnWebViewFailAction=[]
OnWebViewStopAction=[]
; Navigation State Actions
OnStateChangeAction=[]
OnUrlChangeAction=[]
OnPageLoadStartAction=[]
OnPageLoadingAction=[]
OnPageDOMLoadAction=[]
OnPageFirstLoadAction=[]
OnPageReloadAction=[]
OnPageLoadFinishAction=[]
---
; WebView Commands
[!CommandMeasure WebView2 "WebView Start"]
[!CommandMeasure WebView2 "WebView Stop"]
[!CommandMeasure WebView2 "WebView Restart"]
; Navigation Commands
[!CommandMeasure WebView2 "Navigate Home"]
[!CommandMeasure WebView2 "Navigate Back"]
[!CommandMeasure WebView2 "Navigate Forward"]
[!CommandMeasure WebView2 "Navigate Reload"]
[!CommandMeasure WebView2 "Navigate Stop"]
[!CommandMeasure WebView2 "Navigate http://www.example.com"]
; Open Commands
[!CommandMeasure WebView2 "Open DevTools"]
[!CommandMeasure WebView2 "Open TaskManager"]
; Execute Commands
[!CommandMeasure WebView2 "Execute alert('Hello Rainmeter!')"]
[!CommandMeasure WebView2 "Execute path\to\file.js"]
;Section Variables
[WebView2:CallJS('alert("Example script")')]
;User Data Folder Path
C:\Users\User\AppData\Local\Temp\RainmeterWebView2\
📂 User Data Folder
📁 RainmeterWebView2\
├── 📁 EBWebView\
│ └── 📁 [WebView2 Data]\
├── 📁 Extensions\
│ └── Extensions.ini
│ └── 📁 MyExtension\
│ └── 📁 MyOtherExtension\
└── UserSettings.ini
When the plugin loads for the first time, a user data folder (UDP) is created. This folder contains all the data related to WebView2.
Path: C:\Users\User\AppData\Local\Temp\RainmeterWebView2\
If deleted, the folder will be re-created the next time the plugin is loaded.
To delete all your WebView2 data, it is recommended to delete RainmeterWebView2\EBWebView\ instead. This way you preserve your User Settings File and Extensions.
💡 IMPORTANT:
- Exit Rainmeter before modifying anything in this folder, failing to do so will make WebView2 instances fail to start.
- Restart Rainmeter if this happens.
There are certain settings that affect all WebView2 instances, for this reason they can't be exposed through the measure's options.
Such settings can be found in a file called UserSettings.ini.
Path: C:\Users\User\AppData\Local\Temp\RainmeterWebView2\UserSettings.ini
Options
| Option | Description | Default |
|---|---|---|
| Environment Options | ||
Extensions |
Allows to use extensions. | false |
FluentOverlayScrollBars |
Enable Fluent Overlay scrollbars. | true |
TrackingPrevention |
Enables Microsoft Edge tracking prevention. | true |
BrowserArguments |
Additional command-line arguments passed to the WebView2 browser process. | --allow-file-access-from-files |
BrowserLocale |
Sets the browser UI locale. Use system to follow the OS language.Locales need to be in the format of BCP 47 Language Tags. e.g: en-US es-MX fr-FRA list of locales can be found here |
system |
| Controller Options | ||
PrivateMode |
Enables private mode, also commonly called incognito mode. | false |
ScriptLocale |
Locale used for JavaScript APIs such as date, time, and number formatting. Use system to follow the OS language.Locales need to be in the format of BCP 47 Language Tags. e.g: en-US es-MX fr-FRA list of locales can be found here |
system |
| Core Options | ||
StatusBar |
Shows or hides the browser status bar. | true |
PinchZoom |
Enables pinch-to-zoom gestures. | true |
SwipeNavigation |
Enables swipe gestures for back and forward navigation. | true |
SmartScreen |
Enables SmartScreen protection. More info. | true |
| Profile Options | ||
DownloadsFolderPath |
Custom folder path where downloaded files are saved. Empty uses the system default. | |
ColorScheme |
Controls the browser color scheme (light, dark, or system). |
system |
PasswordAutoSave |
Allows automatic saving of passwords. | false |
GeneralAutoFill |
Enables form autofill for non-password fields. | true |
UserSettings.ini
[Environment]
Extensions = false
FluentOverlayScrollBars = true
TrackingPrevention = true
BrowserLocale = system
BrowserArguments = --allow-file-access-from-files
[Controller]
ScriptLocale = system
PrivateMode = false
[Core]
StatusBar = true
PinchZoom = true
SwipeNavigation = true
SmartScreen = true
[Profile]
DownloadsFolderPath =
ColorScheme = system
PasswordAutoSave = false
GeneralAutoFill = true
Using extensions on WebView2 is now possible!
Unfortunately, this feature doesn't come without limitations.
Limitations
- Extensions' UI may not show up at all, or they may show up on the DevTools(F5) window.
⚠️ Important:
- Manipulating Extensions require Rainmeter to be exited.
Path: C:\Users\User\AppData\Local\Temp\RainmeterWebView2\Extensions\
Installing Extensions
To install an extension:
- Exit Rainmeter
- Go to
UserSettings.iniand enable extensions ->Extensions=true - Drop an unpacked extension folder inside the
Extensions\folder - Start Rainmeter
- Done
On Rainmeter, WebView2: "Extension Name" extension installed. will be logged.
Once an extension is installed, a new ini file will be created at Extensions\Extensions.ini, where you can control your extensions.
⚠️ Important:
- Extensions must be unpacked folders.
Toggling Extensions
To enable\disable an extension:
- Exit Rainmeter
- Open
Extensions\Extensions.ini - Find
[YourExtensionFolderName]section - Set
Enabled=falseorEnabled=false - Save the file.
- Launch Rainmeter
- Done
On Rainmeter, WebView2: "Extension Name" extension enabled\disabled. will be logged.
Unninstalling Extensions
To uninstall an extension:
- Exit Rainmeter
- Open
Extensions\Extensions.ini - Find
[YourExtensionFolderName]section - Set
Uninstall=true - Save the file
- Launch Rainmeter
- Manually remove the extension's unpacked folder from
Extensions\ - Done
On Rainmeter, WebView2: "Extension Name" extension removed. will be logged.
⚠️ Important:
- Uninstalling an extension will automatically delete its
[section]fromExtensions.ini, but will not remove its folder from theExtensions\folder.- If the folder is not manually removed after uninstalling the extension, it will be automatically re-installed the next time you launch Rainmeter.
Extensions.ini
Path: C:\Users\User\AppData\Local\Temp\RainmeterWebView2\Extensions\Extensions.ini
[TheExtensionFolderName]
ID = theextensionid
Name = The Extension Name
Enabled = true
Uninstall = false
⚠️ Important:
- You can only modify
EnabledandUnninstall, other options are informative only.- Modifying or deleting
IDwill cause the extension to be reinstalled.- Modifying or deleting
Namewon't do anything, the name will be restored next time the plugin loads.
Your JavaScript can respond to Rainmeter events:
// Called once when navigation starts
window.OnInitialize = function() {
console.log("🚀 WebView initialized!");
RainmeterAPI.Bang('[!Log "🚀 WebView initialized!"]')
};
// Called on every Rainmeter update
window.OnUpdate = function() {
const now = new Date().toLocaleTimeString();
updateSomething(now);
};Use section variables to call any JavaScript function:
[MeterTemperature]
Meter=String
Text=Current temp: [WebView2:CallJS('getTemperature')]°C
DynamicVariables=1// In your HTML
window.getTemperature = function() {
return 72;
};
⚠️ Note: JavaScript execution is asynchronous, so there's a 1-update delay between JS return and Rainmeter display. This is normal!
From inline one-liner strings:
[WebView2]
Measure=Plugin
Plugin=WebView2
URL=https://example.com/
OnPageLoadFinishAction=[!CommandMeasure WebView2 "Execute alert('script executed'); console.log('hola!');"]From files:
// @Resources\script.js
alert('script executed from file');
console.log('hola!');; skin.ini
[WebView2]
Measure=Plugin
Plugin=WebView2
URL=https://example.com/
OnPageLoadFinishAction=[!CommandMeasure WebView2 "Execute #@#script.js"]Dragging elements with app-region: drag; set up will move the skin window. Right clicking these elements also opens the Skin Menu.
body {
app-region: drag;
}
button {
app-region: no-drag;
}When using drag on a div or any other container, you need to manually set no-drag on the interactable children of that container, otherwise they won't work properly.
This CSS style is being used on the YoutubePlayer example skin.
Access Rainmeter's full power from JavaScript:
// Read from your measure
const refreshRate = await RainmeterAPI.ReadInt('UpdateRate', 1000);
const siteName = await RainmeterAPI.ReadString('SiteName', 'Default');
// Read from other sections
const cpuUsage = await RainmeterAPI.ReadStringFromSection('MeasureCPU', 'Value', '0');// Set variables
await RainmeterAPI.Bang('!SetVariable MyVar "Hello World"');
// Control skins
await RainmeterAPI.Bang('!ActivateConfig "MySkin" "Variant.ini"');
// Update meters
await RainmeterAPI.Bang('!UpdateMeter MeterName');
await RainmeterAPI.Bang('!Redraw');const skinName = await RainmeterAPI.SkinName;
const measureName = await RainmeterAPI.MeasureName;
// Replace variables
const path = await RainmeterAPI.ReplaceVariables('#@#images/logo.png');
// Get variable values
const theme = await RainmeterAPI.GetVariable('CurrentTheme');await RainmeterAPI.Log('Debug info', 'DEBUG');
await RainmeterAPI.Log('Warning message', 'WARNING');
await RainmeterAPI.Log('Error occurred', 'ERROR');📚 Click to see all available methods
Reading Options
ReadString(option, defaultValue)→Promise<string>ReadInt(option, defaultValue)→Promise<number>ReadDouble(option, defaultValue)→Promise<number>ReadFormula(option, defaultValue)→Promise<number>ReadPath(option, defaultValue)→Promise<string>
Reading from Sections
ReadStringFromSection(section, option, defaultValue)→Promise<string>ReadIntFromSection(section, option, defaultValue)→Promise<number>ReadDoubleFromSection(section, option, defaultValue)→Promise<number>ReadFormulaFromSection(section, option, defaultValue)→Promise<number>
Utility Functions
ReplaceVariables(text)→Promise<string>GetVariable(variableName)→Promise<string>PathToAbsolute(relativePath)→Promise<string>Bang(command)→Promise<void>Log(message, level)→Promise<void>
Properties
MeasureName→Promise<string>SkinName→Promise<string>SkinWindowHandle→Promise<string>SettingsFile→Promise<string>
There are currently two ways to make WebView2 skins draggable.
Using Clickthrough
Clickthrough is a powerful option that allows mouse input to pass through the WebView2 window to the skin layer behind it, effectively making the window invisible to mouse interactions.
A new value, 2, was recently added. This makes Clickthrough toggleable by holding the CTRL key.
What do you need to do? Nothing.
By default, Clickthrough=2. To drag the skin, simply hold CTRL and drag. This works because you are interacting directly with the skin, not with the WebView2 window.
You can also open the Skin Menu with CTRL + RMB.
Calendar.ini uses this method.
[WebView2]
Measure=Plugin
Plugin=WebView2
X=0
Y=0
W=500
H=500
Clickthrough=2Alternatively, you can use Clickthrough=1 if the skin is only displaying information or images. This removes the need to hold CTRL, but disables all interaction with the WebView2 window.
Clock.ini uses this method.
[WebView2]
Measure=Plugin
Plugin=WebView2
X=0
Y=0
W=500
H=500
Clickthrough=1Using app-region
WebView2 supports the app-region CSS property to define draggable and non-draggable areas on a page, allowing to drag the skin without holding CTRL.
This is a more advanced way to do it, but it will behave much more skin-like than the clickthrough way. YouTubePlayer.ini uses this method.
The property has two values:
dragno-drag
app-region: drag
- Dragging the element moves the window
- Right-click opens the Skin Menu
- All child elements inherit this behavior
Use for non-interactive areas such as backgrounds or title bars.
.titlebar {
app-region: drag;
}app-region: no-drag
- Disables window dragging
- Enables normal mouse interaction
- Overrides inherited
drag
Required for buttons, inputs, sliders, and other interactive elements.
.button,
input {
app-region: no-drag;
}Common Pattern
.window {
app-region: drag;
}
.controls {
app-region: no-drag;
}This keeps the window draggable while preserving UI interactivity.
You can also apply app-region directly in HTML using inline styles.
<div class="titlebar" style="app-region: drag;">
<span>My Skin</span>
<button style="app-region: no-drag;">Nice Button</button>
</div>In this example, the title bar remains draggable while the button stays fully interactive.
Draggable skin example
Load this file using a default WebView2 measure:
index.html
<!DOCTYPE html>
<html>
<style>
body {
background: black;
color: white;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
text-align: center;
<!-- dragging magic -->
app-region: drag;
}
button {
background: green;
color: white;
<!-- no-dragging magic -->
app-region: no-drag;
}
button:hover {
background: lightgreen;
color: black;
cursor: pointer;
}
p { color: grey; }
p:hover { color: white; }
code { color: silver; }
</style>
<body>
<h2>Draggable Skin Example</h1>
<p>Drag the skin</p>
<p>you can drag over everything</p>
<button>but this button.</button>
<p>hold <code>CTRL</code> and <code>drag</code> over the button</p>
</body>
</html>Some APIs and features do not function correctly when accessed via the file:/// protocol.
Previously, this limitation required installing an external http-server to enable proper behavior.
WebView2 now provides a built-in Virtual Host feature, which allows a plugin to be served from a virtual http or https URL, effectively replicating the behavior of a local web server.
In practice, this means you can generate and use a custom virtual URL such as https://my-skin-config-name/ or http://my-skin-config-name/.
This feature provides the following configuration options:
HostSecurity (default: 1)
Specifies which protocol the virtual host will use.
0 - Not secure (http)
1 - Secure (https)
HostOrigin (default: 1)
Defines which config is used as the origin for the virtual host.
0 - Current config
1 - Root config
This setting also determines how Local Storage is scoped:
- Using Current config restricts Local Storage access to the active skin (config) only.
- Using Root config allows Local Storage to be shared across all skins (configs) under the same root, which is useful for suites.
HostPath (default: "")
Specifies the path to the folder containing your file.html.
Example: HostPath = #@#
When this option is set, the plugin enables the Virtual Host feature and generates a virtual URL mapped to the specified folder. The resulting URL uses a protocol defined by HostSecurity and a host name derived from the HostOrigin setting.
How it works:
The plugin detects that a Virtual Host should be used when the HostPath option points to a valid folder. Once detected, it initializes the virtual host and generates a new base URL that can be used to access the mapped files.
Example 1: Current Config as Origin (Isolated Storage)
Assume the following code belongs to the Illustro\Clock skin (config):
; HostSecurity:
; 0 = http (insecure context)
; 1 = https (secure context)
; Using HTTPS allows access to APIs that may be blocked by CORS.
HostSecurity=1
; HostOrigin:
; 0 = current config only
; 1 = root config
; Using current config isolates storage to this skin
HostOrigin=0
; Folder containing the HTML and assets
HostPath=#@#
URL=index.htmlIn this case:
-
The protocol is set to
https -
The origin is derived from the current config:
Illustro\Clock -
The
index.htmlfile resides in the@Resourcesfolder
The generated base URL will be:
https://illustro-clock/
You can navigate to the page explicitly:
URL=https://illustro-clock/index.htmlOr implicitly:
URL=index.htmlWhen using a Virtual Host, the URL option automatically resolves relative paths against the generated virtual host URL.
Local Storage will be isolated to the Illustro\Clock skin, meaning it is scoped to the following origin:
https://illustro-clock/
Example 2: Root Config as Origin (Shared Storage)
Assume the following code belongs to the Illustro\Clock skin (config):
HostSecurity=0
HostOrigin=1
HostPath=#@#Clock\
URL=clock.htmlIn this case:
-
The protocol is set to
http -
The origin is derived from the root config:
Illustro -
The
clock.htmlfile resides in the@Resources\Clockfolder
The generated base URL will be:
http://illustro/
You can navigate to the page explicitly:
URL=http://illustro/clock.htmlOr implicitly:
URL=clock.htmlLocal Storage will be shared across all skins (configs) that belong to the Illustro root config. In other words, Local Storage is scoped to:
http://illustro/
Practical Example
Assume the following skin structure:
📁 MyRootConfig\
├── 📁 @Resources\
│ └── index.html
└── MyRootConfigIni.ini
MyRootConfigIni.ini
[Rainmeter]
Update=1000
[WebView2]
Measure=Plugin
Plugin=WebView2
HostPath=#@#
Url=Index.html
W=300
H=300
[WebView2BG]
Meter=Image
W=300
H=300
SolidColor=0,0,0,255Index.html
<!DOCTYPE html>
<html>
<style>
body {
color: white;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
text-align: center;
}
</style>
<body>
<h1>Hello, Rainmeter!</h1>
<p>This is a very simple HTML page.</p>
<p>Hold CTRL to drag the skin</p>
<p>Press CTRL + RMB to open SkinMenu.</p>
</body>
</html>After loading the skin, check the skins tab on the About window. You'll see that the measure is returning a URL as its string value, which is https://myrootconfig/Index.html.
By default, HostSecurity=1, therefore, as seen on the URL, it's using the https protocol.
Also, HostOrigin=1 by default, but in this example it doesn't matter given our skin's structure. This skin has only a root config, so that's the only host name we can use. Which is the myrootconfig/ part on the URL.
If we now open DevTools (press F12 inside the WebView window) and go to the Aplication tab, then go to Storage -> Local storage on the left side panel, we'll see that our https://myrootconfig origin is listed.
Clicking on our origin will show all the key-value pairs that are stored. Obviously it is empty if you haven't saved anything yet. Check the storage for YoutubePlayer example skin instead.
Now try using this skin's structure and play with both HostSecurity and HostOrigin.
📁 MyRootConfig\
├── 📁 @Resources\
│ └── index.html
├── 📁 MyOtherConfig\
│ └── MyOtherConfigIni.ini
└── MyRootConfigIni.ini
Try setting HostOrigin=0 on both configs, you'll see the same https://myrootconfig origin on DevTools for both configs, which means they share local storage.
If you then set HostOrigin=1 on MyOtherConfig, you'll see its origin is now https://myrootconfig-myotherconfig, which means its local storage is not shared.
The plugin includes ready-to-use example skins:
|
🕐 Clock Animated liquid clock with smooth animations |
📅 Calendar Interactive month view calendar |
⚙️ Config Reader Read options from measures and sections |
🔧 Utilities Demonstrate all API functions |
Youtube Player iFrame API example |
To explore examples:
- Install the
.rmskinpackage - Check your Rainmeter skins folder
- Load example skins from Rainmeter manager
For Developers: Build Instructions
- Visual Studio 2022 with C++ desktop development
- Windows SDK
- PowerShell 5.1+
# Clone repository
git clone https://github.com/yourusername/WebView2.git
cd WebView2
# Open in Visual Studio
start WebView2-Plugin.sln
# Build with PowerShell
powershell -ExecutionPolicy Bypass -Command "& {. .\Build-CPP.ps1; Dist -major 0 -minor 0 -patch 3}"📁 dist/
├── 📁 x64/
│ └── WebView2.dll
├── 📁 x32/
│ └── WebView2.dll
├── WebView2_v0.0.3_x64_x86_dll.zip
└── WebView2_v0.0.3_Alpha.rmskin
❌ "WebView2 Runtime is not installed"
Solution: Install WebView2 Runtime
Windows 11 has it pre-installed. For Windows 10, download and run the installer.
❌ "Failed to create WebView2 controller"
Try these steps:
- ✅ Right-click skin → Refresh skin
- ✅ Restart Rainmeter completely
- ✅ Verify WebView2 Runtime is installed
- ✅ Check Windows Event Viewer for detailed errors
❌ "RainmeterAPI is not defined" in JavaScript
Solution: Wait for page to load before accessing API:
document.addEventListener('DOMContentLoaded', () => {
// Now you can use RainmeterAPI
RainmeterAPI.Log('Page loaded!', 'DEBUG');
});❌ WebView not visible
Checklist:
- ✅ Ensure
Hidden=0in your measure (default is 0) - ✅ Check URL path is correct
- ✅ Verify HTML file exists
- ✅ Look for errors in Rainmeter log
- ✅ Try:
[!CommandMeasure MeasureName "Open DevTools"]to debug
Transparency tip: The WebView has transparent background by default. Use background: transparent; in your CSS.
MIT License - Free to use, modify, and distribute
See LICENSE file for full details
We welcome contributions! Here's how:
|
1. Fork 🍴 Fork this repo |
2. Branch 🌿 Create feature branch |
3. Code 💻 Make your changes |
4. Commit 📝 Commit with clear message |
5. PR 🚀 Open Pull Request |
git checkout -b feature/AmazingFeature
git commit -m 'Add some AmazingFeature'
git push origin feature/AmazingFeatureBuilt with powerful tools and inspired by the community
Microsoft Edge WebView2 • Rainmeter API • Rainmeter Community