Building a Plug‑in‑Based Resource Manager for Web Games and Live Events
This article presents a plug‑in‑based, WebWorker‑enabled resource manager for web games and live‑stream activities, detailing its background, research of existing solutions, overall design, core modules, external APIs, and practical usage examples such as preloading, modular bundles, and image conversion.
Background
Applications consist of code and resources (configuration files, icons, images, fonts, etc.) that affect package size and runtime performance. In social live‑stream development two scenarios require strong resource management: game development, which uses many media assets, and activity operations that rely on preloading and caching to improve page performance and user experience.
Research
Typical game frameworks provide comprehensive resource‑management features such as loading, lookup, destruction, caching, multi‑file configuration, process monitoring, modularization, preloading, and custom handling. Existing solutions fall into two categories: resource‑preloading libraries and full‑featured game resource managers. Many are tightly coupled to specific engines or lack support for pre‑request scenarios.
Overall Design
The proposed manager is engine‑agnostic and built on a plug‑in architecture. Core functionality resides in a central module, while optional features (preloading, conversion, caching) are added as plug‑ins, keeping the main bundle size stable.
Dependency Capabilities
The manager relies on an Extension module for plug‑in registration and on WebWorker for multi‑threaded resource parsing and conversion, preventing main‑thread stalls during heavy asset processing.
import WorkController from 'music/WorkController';
const MAX_WORKER_NUM = navigator.hardwareConcurrency || 6;
const loadBufferImageCode = `
async function loadBufferImage(url) {
const result = await fetch(url);
if (!result.ok) {
throw new Error('failed to load');
}
const imageBuffer = await result.arrayBuffer();
return imageBuffer;
}
onmessage = async (e) => {
const { data: { uuid, id } } = e;
try {
const bufferImage = await loadBufferImage(e.data.data[0]);
postMessage({ data: bufferImage, uuid, id }, [bufferImage]);
} catch (error) {
postMessage({ error, uuid, id });
}
};
`;
let worker = WorkController.workerPool.pop();
if (!worker && WorkController.WorkersNumber < MAX_WORKER_NUM) {
const workerURL = URL.createObjectURL(new Blob([loadBufferImageCode], { type: 'application/javascript' }));
WorkController.WorkersNumber++;
worker = new Worker(workerURL);
worker.addEventListener('message', (event) => {
WorkController.complete(event.data);
WorkController.next();
});
}Core Modules
For each resource type the manager performs detection, mapping, loading, parsing, and caching. Not all steps are mandatory; for example, pre‑request resources skip caching because the browser handles it.
External Interfaces
Two main interfaces are provided: Resource for global assets and Cache for caching. Resources are further divided into Resource and Bundle, enabling both global and modular asset operations.
Feature Usage
Preloading
Preloading uses idle browser time to fetch assets likely needed soon, reducing perceived load time and visual flicker.
const loadAssets = [
{ src: 'https://someurl.png', type: 'IMAGE' },
{ src: 'https://someurl.mp3', type: 'AUDIO' },
{ src: 'http://someurl.mp4', type: 'VIDEO' },
{ src: 'https://someurl.ttf', type: 'FONT', subType: 'ttf' },
{ src: 'https://someurl.json', type: 'JSON' }
];
const LoadingPage = () => {
const [progress, setProgress] = React.useState(0);
React.useEffect(() => {
const load = async () => {
await Resource.loadResource(loadAssets, (p) => {
const num = Number(p.toString().match(/^\d+(?:\.\d{0,2})?/));
setProgress(num * 100);
});
};
load();
}, []);
return <p>Resource loading progress: {progress}%</p>;
};Resource Modularization
Assets can be grouped into bundles (e.g., first‑scene, next‑scene) and loaded on demand to reduce initial download size.
// Add bundles
Resource.addBundle('first-scene', {
mainBg: 'backgroundA.png',
avatar: 'avatarA.png',
font: 'fontA.ttf'
});
Resource.addBundle('next-scene', {
mainBg: 'backgroundB.png',
avatar: 'avatarB.png',
font: 'fontB.ttf'
});
// Load bundles
const firstSceneResource = await Resource.loadBundle('first-scene');
const nextSceneResource = await Resource.loadBundle('next-scene');Resource Conversion
Image conversion plug‑ins can transform assets into formats such as Bitmap, Blob, PixiTexture, etc.
import ResourceImagePlugin from 'resource-image-plugin';
Resource.addPlugin(ResourceImagePlugin);
const res = await Resource.loadResource({
src: 'https://p5.music.126.net/.../image.png',
formatType: 'Bitmap'
});Conclusion
The manager has been deployed in multiple social‑live‑streaming services, providing out‑of‑the‑box asset handling for the Alice.js engine and preloading capabilities for activity pages. Future work includes support for 3D models and intelligent loading strategies.
References
https://github.com/cocos/cocos-engine
https://github.com/pixijs/pixijs
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
