Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction to bevy_cef

bevy_cef is a powerful Bevy plugin that integrates the Chromium Embedded Framework (CEF) into Bevy applications, enabling you to render web content as 3D objects in your game world or as UI overlays.

What is bevy_cef?

bevy_cef bridges the gap between modern web technologies and Bevy's 3D engine by:

  • Embedding webviews as textures on 3D meshes or 2D sprites
  • Supporting bidirectional communication between JavaScript and Bevy systems
  • Providing a multi-process architecture for stability and performance
  • Offering local asset serving through a custom URL scheme
  • Enabling developer tools integration for debugging web content

Key Features

🌐 Web Content Rendering

  • Render any web page as a texture on 3D objects
  • Support for HTML5, CSS3, and modern JavaScript
  • Local file serving via the cef://localhost/ scheme
  • Remote web page loading with full browser compatibility

🔄 Inter-Process Communication (IPC)

  • JS Emit: Send events from JavaScript to Bevy systems
  • Host Emit: Send events from Bevy to JavaScript
  • Bevy Remote Protocol (BRP): Bidirectional RPC communication

🎮 Interactive Controls

  • Keyboard input forwarding to webviews
  • Mouse interaction support
  • Navigation controls (back, forward, refresh)
  • Zoom level management
  • Audio muting capabilities

🔧 Developer Experience

  • Chrome DevTools integration for debugging
  • Hot-reload support for local assets
  • Comprehensive error handling and logging
  • Extensive customization options

Architecture Overview

bevy_cef uses a multi-process architecture similar to modern web browsers:

  • Browser Process: The main Bevy application process
  • Render Process: Separate CEF process for web content rendering
  • IPC Communication: Secure inter-process communication channels

This design ensures stability - if a web page crashes, it won't bring down your entire application.

Use Cases

Game UI

Create rich, responsive game interfaces using familiar web technologies:

#![allow(unused)]
fn main() {
commands.spawn((
    CefWebviewUri::local("ui/main-menu.html"),
    // Render as 2D sprite overlay
));
}

In-World Displays

Embed interactive web content directly in your 3D world:

#![allow(unused)]
fn main() {
commands.spawn((
    CefWebviewUri::new("https://example.com"),
    Mesh3d(meshes.add(Plane3d::new(Vec3::Z, Vec2::ONE))),
    MeshMaterial3d(materials.add(WebviewExtendStandardMaterial::default())),
));
}

Data Visualization

Display real-time data using web-based charting libraries:

#![allow(unused)]
fn main() {
// Load a local HTML file with Chart.js or D3.js
commands.spawn((
    CefWebviewUri::local("charts/dashboard.html"),
    WebviewSize(Vec2::new(1920.0, 1080.0)),
));
}

Development Tools

Integrate web-based development and debugging interfaces directly into your game editor or development build.

Getting Started

Ready to integrate web content into your Bevy application? Check out the Quick Start guide to get up and running in minutes, or dive into Basic Concepts to understand the fundamental components and systems.

Platform Support

Currently, bevy_cef focuses on macOS development with plans for expanded platform support. The plugin automatically handles CEF framework installation and configuration on supported platforms.

Installation

This guide will help you set up bevy_cef in your Bevy project.

Prerequisites

System Requirements

  • macOS: Currently the primary supported platform
  • Rust: 1.70 or later
  • Bevy: 0.16 or later

Adding bevy_cef to Your Project

1. Add the Dependency

Add bevy_cef to your Cargo.toml:

[dependencies]
bevy = "0.16"
bevy_cef = { version = "0.1", features = ["debug"] }

2. Enable the Plugin

Add the CefPlugin to your Bevy app:

use bevy::prelude::*;
use bevy_cef::prelude::*;

fn main() {
    App::new()
        .add_plugins((DefaultPlugins, CefPlugin))
        .run();
}

Automatic Setup with Debug Feature

When you enable the debug feature (recommended for development), the build system automatically handles everything for you:

Downloads and extracts CEF framework to $HOME/.local/share/cef
Installs the bevy_cef_debug_render_process tool
Installs the export-cef-dir tool
Configures all necessary environment variables

Simply run your project:

cargo run

That's it! No manual installation required.

Production Builds

For production builds where you want to minimize dependencies, you can omit the debug feature:

[dependencies]
bevy = "0.16"
bevy_cef = "0.1"  # No debug feature

However, you'll need to ensure the CEF framework is available at runtime. The debug feature is recommended for most development scenarios.

Verification

To verify your installation is working correctly, try running this simple example:

use bevy::prelude::*;
use bevy_cef::prelude::*;

fn main() {
    App::new()
        .add_plugins((DefaultPlugins, CefPlugin))
        .add_systems(Startup, setup)
        .run();
}

fn setup(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<WebviewExtendStandardMaterial>>,
) {
    // Camera
    commands.spawn((
        Camera3d::default(),
        Transform::from_xyz(0.0, 0.0, 3.0).looking_at(Vec3::ZERO, Vec3::Y),
    ));

    // Light
    commands.spawn((
        DirectionalLight::default(),
        Transform::from_xyz(1.0, 1.0, 1.0).looking_at(Vec3::ZERO, Vec3::Y),
    ));

    // Webview
    commands.spawn((
        CefWebviewUri::new("https://bevy.org"),
        Mesh3d(meshes.add(Plane3d::new(Vec3::Z, Vec2::ONE))),
        MeshMaterial3d(materials.add(WebviewExtendStandardMaterial::default())),
    ));
}

If you see the Bevy website rendered on a plane in your 3D scene, your installation is successful!

Troubleshooting

Common Installation Issues

Build Fails with Debug Feature

  • Ensure you have internet connection for CEF download
  • Check available disk space in $HOME/.local/share/
  • Try cleaning and rebuilding: cargo clean && cargo build

CEF Framework Issues

  • The debug feature handles CEF installation automatically
  • If issues persist, delete $HOME/.local/share/cef and rebuild

Linker Errors on macOS

  • Verify Xcode Command Line Tools are installed: xcode-select --install
  • The debug feature should handle DYLD environment variables automatically

For more troubleshooting information, see the Troubleshooting section.

Next Steps

Now that you have bevy_cef installed, check out:

Quick Start

Get up and running with bevy_cef in just a few minutes! This guide will walk you through creating your first webview-enabled Bevy application.

Create a New Project

Start by creating a new Bevy project:

cargo new my_webview_app
cd my_webview_app

Add Dependencies

Add bevy_cef to your Cargo.toml:

[dependencies]
bevy = "0.16"
bevy_cef = { version = "0.1", features = ["debug"] }

Your First Webview

Replace the contents of src/main.rs with:

use bevy::prelude::*;
use bevy_cef::prelude::*;

fn main() {
    App::new()
        .add_plugins((DefaultPlugins, CefPlugin))
        .add_systems(Startup, setup)
        .run();
}

fn setup(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<WebviewExtendStandardMaterial>>,
) {
    // Spawn a camera
    commands.spawn((
        Camera3d::default(),
        Transform::from_translation(Vec3::new(0.0, 0.0, 3.0))
            .looking_at(Vec3::ZERO, Vec3::Y),
    ));

    // Spawn a light
    commands.spawn((
        DirectionalLight::default(),
        Transform::from_translation(Vec3::new(1.0, 1.0, 1.0))
            .looking_at(Vec3::ZERO, Vec3::Y),
    ));

    // Spawn a webview on a 3D plane
    commands.spawn((
        CefWebviewUri::new("https://bevy.org"),
        Mesh3d(meshes.add(Plane3d::new(Vec3::Z, Vec2::ONE))),
        MeshMaterial3d(materials.add(WebviewExtendStandardMaterial::default())),
    ));
}

Run Your Application

cargo run

That's it! You should see the Bevy website rendered on a 3D plane in your application. The first run might take a moment as bevy_cef downloads and sets up the CEF framework automatically.

What Just Happened?

Let's break down the key components:

1. CefPlugin

#![allow(unused)]
fn main() {
.add_plugins((DefaultPlugins, CefPlugin))
}

The CefPlugin initializes the CEF framework and sets up all necessary systems for webview rendering.

2. CefWebviewUri

#![allow(unused)]
fn main() {
CefWebviewUri::new("https://bevy.org")
}

This component specifies what web content to load. You can use:

  • Remote URLs: "https://example.com"
  • Local files: CefWebviewUri::local("index.html")

3. WebviewExtendStandardMaterial

#![allow(unused)]
fn main() {
MeshMaterial3d(materials.add(WebviewExtendStandardMaterial::default()))
}

This special material renders the webview content as a texture on your 3D mesh.

Try Local Content

Create an assets/ directory and add a simple HTML file:

mkdir assets

Create assets/hello.html:

<!DOCTYPE html>
<html>
<head>
    <title>Hello bevy_cef!</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            background: linear-gradient(45deg, #ff6b6b, #4ecdc4);
            color: white;
            text-align: center;
            padding: 50px;
        }
    </style>
</head>
<body>
    <h1>Hello from bevy_cef! 🎮</h1>
    <p>This HTML is rendered directly on a 3D mesh!</p>
    <p>Current time: <span id="time"></span></p>
    
    <script>
        function updateTime() {
            document.getElementById('time').textContent = new Date().toLocaleTimeString();
        }
        setInterval(updateTime, 1000);
        updateTime();
    </script>
</body>
</html>

Update your webview URI in main.rs:

#![allow(unused)]
fn main() {
CefWebviewUri::local("hello.html"),  // Load local file
}

Run again:

cargo run

You'll see your custom HTML content with a live-updating clock!

Next Steps

Explore More Features

Try More Examples

  • Navigation: Add back/forward buttons
  • Zoom Controls: Implement zoom in/out functionality
  • Multiple Webviews: Render different content on multiple objects

Learn the Architecture

Common First Steps

Adding Interaction

Make your webview interactive by enabling input:

#![allow(unused)]
fn main() {
// In your setup system, also spawn input handling
commands.spawn((
    CefWebviewUri::local("interactive.html"),
    WebviewSize(Vec2::new(800.0, 600.0)),
    // Webview will automatically receive keyboard and mouse input
    Mesh3d(meshes.add(Plane3d::new(Vec3::Z, Vec2::ONE))),
    MeshMaterial3d(materials.add(WebviewExtendStandardMaterial::default())),
));
}

Sizing Your Webview

Control the resolution of your webview:

#![allow(unused)]
fn main() {
WebviewSize(Vec2::new(1920.0, 1080.0)),  // High resolution
WebviewSize(Vec2::new(800.0, 600.0)),    // Standard resolution
}

Multiple Webviews

You can have multiple webviews in the same scene:

#![allow(unused)]
fn main() {
// First webview
commands.spawn((
    CefWebviewUri::new("https://news.ycombinator.com"),
    Transform::from_translation(Vec3::new(-2.0, 0.0, 0.0)),
    Mesh3d(meshes.add(Plane3d::new(Vec3::Z, Vec2::ONE))),
    MeshMaterial3d(materials.add(WebviewExtendStandardMaterial::default())),
));

// Second webview  
commands.spawn((
    CefWebviewUri::local("dashboard.html"),
    Transform::from_translation(Vec3::new(2.0, 0.0, 0.0)),
    Mesh3d(meshes.add(Plane3d::new(Vec3::Z, Vec2::ONE))),
    MeshMaterial3d(materials.add(WebviewExtendStandardMaterial::default())),
));
}

You're now ready to build amazing applications that blend 3D graphics with modern web technologies!

Basic Concepts

Understanding these fundamental concepts will help you make the most of bevy_cef in your projects.

Multi-Process Architecture

bevy_cef follows a multi-process architecture similar to modern web browsers:

Browser Process

  • Main Application: Your Bevy game/app runs here
  • CEF Management: Handles browser creation and management
  • IPC Coordination: Manages communication with render processes

Render Process

  • Web Content: Each webview runs in a separate process
  • Isolation: Crashes in web content don't affect your main application
  • Sandboxing: Enhanced security through process separation

Benefits

  • Stability: Web content crashes won't crash your game
  • Performance: CPU-intensive web content won't block your game loop
  • Security: Sandboxed execution of untrusted web content

Core Components

bevy_cef provides several key components that work together:

CefWebviewUri

The primary component that defines what web content to display:

#![allow(unused)]
fn main() {
// Remote web page
CefWebviewUri::new("https://example.com")

// Local HTML file from assets/
CefWebviewUri::local("ui/menu.html")

// Equivalent to above
CefWebviewUri::new("cef://localhost/ui/menu.html")
}

WebviewSize

Controls the rendering resolution of the webview (not the 3D object size):

#![allow(unused)]
fn main() {
WebviewSize(Vec2::new(1920.0, 1080.0))  // High resolution
WebviewSize(Vec2::new(800.0, 600.0))    // Standard resolution
WebviewSize::default()                   // 800x800 pixels
}

Material Integration

Webviews integrate with Bevy's material system:

#![allow(unused)]
fn main() {
// Standard material with webview texture
WebviewExtendStandardMaterial::default()

// Custom material properties
WebviewExtendStandardMaterial {
    base: StandardMaterial {
        unlit: true,
        emissive: Color::WHITE.into(),
        ..default()
    },
    ..default()
}
}

Component Requirements

When you add a CefWebviewUri component, bevy_cef automatically requires several other components:

#![allow(unused)]
fn main() {
#[require(WebviewSize, ZoomLevel, AudioMuted)]
pub struct CefWebviewUri(pub String);
}

This means every webview entity automatically gets:

  • WebviewSize: Default 800x800 resolution
  • ZoomLevel: Default zoom (0.0 = browser default)
  • AudioMuted: Default unmuted (false)

Rendering Modes

bevy_cef supports different rendering approaches:

3D Mesh Rendering

Render web content on 3D objects in your world:

#![allow(unused)]
fn main() {
commands.spawn((
    CefWebviewUri::local("interface.html"),
    Mesh3d(meshes.add(Plane3d::new(Vec3::Z, Vec2::ONE))),
    MeshMaterial3d(materials.add(WebviewExtendStandardMaterial::default())),
));
}

2D Sprite Rendering

Render web content as UI overlays:

#![allow(unused)]
fn main() {
commands.spawn((
    CefWebviewUri::local("hud.html"),
    Sprite::default(),
    WebviewSpriteMaterial::default(),
));
}

Local Asset Serving

bevy_cef includes a built-in web server that serves files from your assets/ directory:

Custom Scheme

  • Scheme: cef://localhost/
  • Path Mapping: assets/ directory maps to the root
  • Example: assets/ui/menu.htmlcef://localhost/ui/menu.html

Supported File Types

  • HTML, CSS, JavaScript
  • Images (PNG, JPG, SVG, etc.)
  • Fonts (WOFF, TTF, etc.)
  • JSON, XML, and other data files

Asset Organization

assets/
├── ui/
│   ├── menu.html
│   ├── styles.css
│   └── script.js
├── images/
│   └── logo.png
└── data/
    └── config.json

Inter-Process Communication (IPC)

bevy_cef provides three communication patterns:

1. JavaScript to Bevy (JS Emit)

Send events from web content to Bevy systems:

// In your HTML/JavaScript
window.cef.emit('player_action', { action: 'jump', power: 10 });
fn main(){
    App::new()
        .add_plugins(JsEmitEventPlugin::<PlayerAction>::default())
    // ...
}

// In your Bevy system
#[derive(Event, Deserialize)]
struct PlayerAction {
    action: String,
    power: i32,
}

fn handle_player_action(trigger: Trigger<PlayerAction>) {
    let action = trigger.event();
    info!("Player action: {} with power {}", action.action, action.power);
}

2. Bevy to JavaScript (Host Emit)

Send events from Bevy to web content:

#![allow(unused)]
fn main() {
// In your Bevy system
commands.entity(webview_entity).trigger(HostEmitEvent {
    event_name: "score_update".to_string(),
    data: json!({ "score": 1000, "level": 3 }),
});
}
// In your HTML/JavaScript
window.cef.listen('score_update', (data) => {
    document.getElementById('score').textContent = data.score;
    document.getElementById('level').textContent = data.level;
});

3. Bevy Remote Protocol (BRP)

Please see here for about the Bevy Remote Protocol (BRP).

Bidirectional RPC calls for complex interactions:

#![allow(unused)]
fn main() {
// Register BRP method in Bevy
app.add_plugins(RemotePlugin::default().with_method("get_player_stats", get_stats));
}
// Call from JavaScript
const stats = await window.cef.brp({ 
    method: 'get_player_stats',
    params: { playerId: 42 }
});

User Interaction

Input Handling

Webviews automatically receive keyboard and mouse input when focused:

  • Keyboard: All keyboard events are forwarded
  • Mouse: Click, scroll, and hover events work naturally
  • Focus: Multiple webviews can coexist; input goes to the focused one

Control web navigation programmatically:

#![allow(unused)]
fn main() {
commands.entity(webview).trigger(RequestGoBack);
commands.entity(webview).trigger(ReqeustGoForward);
}

Zoom Control

Manage zoom levels per webview:

#![allow(unused)]
fn main() {
// Set zoom level (0.0 = default, positive = zoom in, negative = zoom out)
commands.entity(webview).insert(ZoomLevel(1.2));

// Reset to default zoom
commands.entity(webview).insert(ZoomLevel(0.0));
}

Developer Experience

Developer Tools

Access Chrome DevTools for debugging:

#![allow(unused)]
fn main() {
// Show developer tools for a webview
commands.entity(webview).trigger(RequestShowDevTool);

// Close developer tools
commands.entity(webview).trigger(RequestCloseDevtool);
}

Hot Reload

Local assets automatically reload when changed during development.

Best Practices

Component Organization

#![allow(unused)]
fn main() {
// Good: Group related components
commands.spawn((
    // The uri convert to `cef://localhost/index.html`
    CefWebviewUri::local("index.html"),
    WebviewSize(Vec2::new(1920.0, 200.0)),
    ZoomLevel(0.8),
    AudioMuted(true),
    Transform::from_translation(Vec3::new(0.0, 5.0, 0.0)),
    Mesh3d(meshes.add(Quad::new(Vec2::new(4.0, 1.0)))),
    MeshMaterial3d(materials.add(WebviewExtendStandardMaterial::default())),
));
}

Next Steps

Now that you understand the basic concepts:

Core Components

bevy_cef provides several essential components that control webview behavior. Understanding these components is crucial for effective use of the library.

Component Overview

ComponentPurposeDefault ValueRequired
CefWebviewUriSpecifies web content URLNone✅ Primary
WebviewSizeControls rendering resolution800×800✅ Auto-added
ZoomLevelControls webview zoom0.0 (default)✅ Auto-added
AudioMutedControls audio outputfalse (unmuted)✅ Auto-added
HostWindowParent window specificationPrimary window❌ Optional

CefWebviewUri

The primary component that defines what web content to display.

Usage

#![allow(unused)]
fn main() {
use bevy_cef::prelude::*;

// Remote web page
let webview = CefWebviewUri::new("https://example.com");

// Local HTML file from assets/ directory
let webview = CefWebviewUri::local("ui/menu.html");

// Equivalent to local() method
let webview = CefWebviewUri::new("cef://localhost/ui/menu.html");
}

Implementation Details

#![allow(unused)]
fn main() {
#[derive(Component, Debug, Clone, PartialEq, Eq, Hash, Reflect)]
#[require(WebviewSize, ZoomLevel, AudioMuted)]
pub struct CefWebviewUri(pub String);
}

The #[require(...)] attribute ensures that every webview automatically gets the essential supporting components.

Methods

  • new(uri): Create with any valid URL (http, https, cef://localhost/)
  • local(path): Create with local file path, automatically prefixed with cef://localhost/

Local File Serving

When using local files, bevy_cef serves them through a custom scheme:

  • Scheme: cef://localhost/
  • Root Directory: Your project's assets/ folder
  • Path Resolution: Relative paths from assets/ root

Example File Structure:

assets/
├── index.html          → cef://localhost/index.html
├── ui/
│   ├── menu.html      → cef://localhost/ui/menu.html
│   └── styles.css     → cef://localhost/ui/styles.css
└── js/
    └── app.js         → cef://localhost/js/app.js

WebviewSize

Controls the internal rendering resolution of the webview, independent of the 3D object size.

Usage

#![allow(unused)]
fn main() {
use bevy::math::Vec2;

// High resolution webview
WebviewSize(Vec2::new(1920.0, 1080.0))

// Standard resolution
WebviewSize(Vec2::new(800.0, 600.0))

// Square webview
WebviewSize(Vec2::splat(512.0))

// Default size
WebviewSize::default () // 800×800
}

Performance Considerations

  • Higher Resolution: Better quality, more memory usage
  • Lower Resolution: Better performance, potential pixelation
  • Aspect Ratio: Match your 3D mesh proportions for best results
#![allow(unused)]
fn main() {
// Example: Widescreen webview for cinematic content
commands.spawn((
CefWebviewUri::local("video-player.html"),
WebviewSize(Vec2::new(1920.0, 800.0)),  // 21:9 aspect ratio
Mesh3d(meshes.add(Quad::new(Vec2::new(4.8, 2.0)))), // Match aspect in 3D
MeshMaterial3d(materials.add(WebviewExtendStandardMaterial::default ())),
));
}

Dynamic Resizing

You can change webview size at runtime:

#![allow(unused)]
fn main() {
fn resize_webview(
    mut webviews: Query<&mut WebviewSize>,
    keyboard: Res<ButtonInput<KeyCode>>,
) {
    if keyboard.just_pressed(KeyCode::KeyR) {
        for mut size in webviews.iter_mut() {
            size.0 *= 1.5; // Increase resolution by 50%
        }
    }
}
}

ZoomLevel

Controls the zoom level of web content within the webview.

Usage

#![allow(unused)]
fn main() {
// Default zoom (browser default)
ZoomLevel(0.0)

// Zoom in 20%
ZoomLevel(1.2)

// Zoom out 20%
ZoomLevel(0.8)

// Maximum zoom in
ZoomLevel(3.0)

// Maximum zoom out  
ZoomLevel(0.25)
}

Zoom Behavior

  • 0.0: Browser default zoom level
  • Positive values: Zoom in (1.2 = 120% of default)
  • Negative values: Zoom out (0.8 = 80% of default)
  • Range: Typically 0.25 to 3.0 (25% to 300%)

Dynamic Zoom Control

#![allow(unused)]
fn main() {
fn zoom_control(
    mut webviews: Query<&mut ZoomLevel>,
    keyboard: Res<ButtonInput<KeyCode>>,
) {
    for mut zoom in webviews.iter_mut() {
        if keyboard.just_pressed(KeyCode::Equal) {
            zoom.0 = (zoom.0 + 0.1).min(3.0); // Zoom in
        }
        if keyboard.just_pressed(KeyCode::Minus) {
            zoom.0 = (zoom.0 - 0.1).max(0.25); // Zoom out
        }
        if keyboard.just_pressed(KeyCode::Digit0) {
            zoom.0 = 0.0; // Reset zoom
        }
    }
}
}

Use Cases

  • Accessibility: Larger text for readability
  • Dense Content: Fit more information in limited space
  • Responsive Design: Adapt to different screen sizes
  • User Preference: Allow users to adjust comfortable viewing size

AudioMuted

Controls whether audio from the webview is muted.

Usage

#![allow(unused)]
fn main() {
// Audio enabled (default)
AudioMuted(false)

// Audio muted
AudioMuted(true)
}

Dynamic Audio Control

#![allow(unused)]
fn main() {
fn toggle_audio(
    mut webviews: Query<&mut AudioMuted>,
    keyboard: Res<ButtonInput<KeyCode>>,
) {
    if keyboard.just_pressed(KeyCode::KeyM) {
        for mut muted in webviews.iter_mut() {
            muted.0 = !muted.0; // Toggle mute state
        }
    }
}
}

Use Cases

  • Background Content: Mute decorative webviews
  • Multiple Webviews: Prevent audio conflicts
  • User Control: Provide mute/unmute functionality
  • Game State: Mute during pause or cutscenes

HostWindow (Optional)

Specifies which Bevy window should be the parent of the webview. If not provided, the primary window is used.

Usage

#![allow(unused)]
fn main() {
// Use primary window (default behavior)
commands.spawn((
CefWebviewUri::local("ui.html"),
// No HostWindow component needed
));

// Specify a particular window
commands.spawn((
CefWebviewUri::local("ui.html"),
HostWindow(secondary_window_entity),
));
}

Multi-Window Applications

#![allow(unused)]
fn main() {
fn setup_multi_window(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<WebviewExtendStandardMaterial>>,
) {
    // Create secondary window
    let secondary_window = commands.spawn(Window {
        title: "Secondary Display".to_string(),
        resolution: (800.0, 600.0).into(),
        ..default()
    }).id();

    // Main webview in primary window
    commands.spawn((
        CefWebviewUri::local("main-ui.html"),
        Mesh3d(meshes.add(Plane3d::new(Vec3::Z, Vec2::ONE))),
        MeshMaterial3d(materials.add(WebviewExtendStandardMaterial::default())),
    ));

    // Secondary webview in secondary window
    commands.spawn((
        CefWebviewUri::local("secondary-ui.html"),
        HostWindow(secondary_window),
        Mesh3d(meshes.add(Plane3d::new(Vec3::Z, Vec2::ONE))),
        MeshMaterial3d(materials.add(WebviewExtendStandardMaterial::default())),
    ));
}
}

Component Combinations

Common Patterns

High-Resolution Interactive Display:

#![allow(unused)]
fn main() {
commands.spawn((
CefWebviewUri::local("dashboard.html"),
WebviewSize(Vec2::new(2560.0, 1440.0)),
ZoomLevel(0.0),
AudioMuted(false),
));
}

Compact Information Panel:

#![allow(unused)]
fn main() {
commands.spawn((
CefWebviewUri::local("info-panel.html"),
WebviewSize(Vec2::new(400.0, 300.0)),
ZoomLevel(0.8),
AudioMuted(true),
));
}

Video Player:

#![allow(unused)]
fn main() {
commands.spawn((
CefWebviewUri::new("https://player.example.com"),
WebviewSize(Vec2::new(1920.0, 1080.0)),
ZoomLevel(0.0),
AudioMuted(false), // Keep audio for video
));
}

Background Decoration:

#![allow(unused)]
fn main() {
commands.spawn((
CefWebviewUri::local("animated-bg.html"),
WebviewSize(Vec2::new(1024.0, 1024.0)),
ZoomLevel(0.0),
AudioMuted(true), // No audio for decoration
));
}

Component Lifecycle

Automatic Requirements

When you add CefWebviewUri, the required components are automatically added with default values:

#![allow(unused)]
fn main() {
// You only need to specify this:
commands.spawn(CefWebviewUri::local("page.html"));

// But the entity automatically gets:
// - WebviewSize(Vec2::splat(800.0))
// - ZoomLevel(0.0)  
// - AudioMuted(false)
}

Manual Override

You can override the defaults by adding components explicitly:

#![allow(unused)]
fn main() {
commands.spawn((
CefWebviewUri::local("page.html"),
WebviewSize(Vec2::new(1024.0, 768.0)), // Override default
ZoomLevel(1.2),                        // Override default
AudioMuted(true),                      // Override default
));
}

Runtime Modification

All components can be modified at runtime through standard Bevy systems:

#![allow(unused)]
fn main() {
fn modify_webview_properties(
    mut query: Query<(&mut WebviewSize, &mut ZoomLevel, &mut AudioMuted)>,
    time: Res<Time>,
) {
    for (mut size, mut zoom, mut muted) in query.iter_mut() {
        // Dynamic effects based on time, input, game state, etc.
        let scale = (time.elapsed_secs().sin() + 1.0) / 2.0;
        zoom.0 = 0.8 + scale * 0.4; // Oscillate between 0.8 and 1.2
    }
}
}

Best Practices

Performance Optimization

#![allow(unused)]
fn main() {
// Good: Appropriate resolution for use case
WebviewSize(Vec2::new(800.0, 600.0))   // Standard UI

// Avoid: Excessive resolution unless needed
WebviewSize(Vec2::new(4096.0, 4096.0)) // Only for high-detail content
}

Memory Management

#![allow(unused)]
fn main() {
// Good: Mute background content
commands.spawn((
CefWebviewUri::local("ambient-display.html"),
AudioMuted(true), // Prevent memory usage for audio processing
));
}

User Experience

#![allow(unused)]
fn main() {
// Good: Consistent zoom across related webviews
let ui_zoom = ZoomLevel(1.1);
for ui_component in ["menu.html", "inventory.html", "settings.html"] {
commands.spawn((
CefWebviewUri::local(ui_component),
ui_zoom,
));
}
}

Next Steps