Как создать виджет для стримов с удалённым управлением
В этой статье мы рассмотрим как по шагам создать виджет для стримов с удалённым управлением. Вы сможете повторить эту статью и получить рабочий виджет.
Подготовка
Все виджеты являются обычными веб-сайтами, которые работают во встроенном браузере OBS Studio. Поэтому для создания виджета вам понадобится стандартный набор инструментов для веб-разработки.
Для упрощения статьи мы не будет использовать никакие веб-фреймворки и реализуем всё на чистом HTML, CSS и JavaScript. Однако, всё ещё будет использовать сборку с помощью Vite (это такая программа, которая упрощает разработку и сборку проектов для последующей публикации).
BunJS
Установите BunJS - современный менеджер пакетов и среду выполнения JavaScript. Инструкции по установке можно найти на официальном сайте.
powershell -c "irm bun.sh/install.ps1 | iex"curl -fsSL https://bun.sh/install | bashVisual Studio Code
Так же вам понадобится VSCode - программа для редактирования кода, в которой мы будет разрабатывать, скачать можно на официальном сайте.
Создание проекта
Создайте новую папку для вашего проекта и откройте её в VSCode. Затем откройте терминал в VSCode (меню Terminal → New Terminal) и выполните команду:
bun create vite . --template vanillaПосле чего несколько раз нажмите Enter, чтобы принять значения по умолчанию.
Вывод терминала после выполнения команды

У вас будет настроен проект, установленные зависимости и запущен локальный сервер для разработки (обычно по адресу http://localhost:5174/).
Откройте этот адрес в браузере, вы должны увидеть стартовую страницу Hello Vite!.
Очистить стартовую страницу, для этого, в VSCode удалим папку public со всем её содержимым, а так же файлы src/counter.js и src/javascript.svg.
Отредактируем файл src/main.js, удалив из него всё содержимое кроме подключения стилей:
import './style.css'А так же, полностью очистим файл src/style.css, оставив его пустым.
Готово! Каждое изменение в файлах будет автоматически отображаться в браузере благодаря Vite. Теперь мы видим пустую белую страницу.
Создание виджета
Разработка виджета будет состоять из двух этапов:
- Создадим визуальное оформление виджета
- Добавим функционал удалённого управления
Визуальное оформление виджета
Оформление виджета максимально индивидуально и зависит от вашей задачи, процесс совершенно ничем не отличается от создания обычного веб-сайта. В этой статье мы создадим простой счётчик ЛБЗ. Он состоит их трёх блоков, в каждом из которых есть прогресс-бар и число выполненных ЛБЗ.
Единственное отличие от обычного веб-сайта - это размеры виджета. Мы будем делать автоматическую подгонку под ширину экрана, это позволит стримеру использовать виджет на на любых разрешениях. Для этого мы установим размер шрифта равным 1% от ширины экрана, а все стили будем указывать в em.
:root {
/* Прозрачный фон */
color-scheme: dark;
background: transparent;
}
body {
/* Устанавливаем размер шрифта в зависимости от ширины окна */
font-size: 1vw;
/* Убираем отступ и скрываем скролл */
margin: 0;
overflow: hidden;
}Теперь создадим общую структуру виджета в файле index.html:
...
<div id="app">
<div class="item item-1">
<header>
<h2>VI - VII уровень</h2>
<p><span class="current">18</span> / 25</p>
<img src="./src/assets/complete.png">
</header>
<div class="progress-bar">
<div class="progress-bar-background"></div>
<div class="progress-bar-pattern"></div>
<div class="progress-bar-blink"></div>
</div>
</div>
</div>
...И дважды продублируем блок div.item, изменив текст заголовка h2, на последний div.item добавим класс completed.
Для оформления нам понадобятся некоторые аасеты из игры, найти их можно в пакете gui.pkg или в репозитории Kurzdor/wot.assets
- Шрифт можно найти по пути
gui/gameface/fonts/Warhelios-Regular.ttf - Детали проггресс бара по пути
/gui/maps/icons/components/progress_bar - Галочка о выполненном этапе по пути
/gui/maps/icons/personalMissions3/QuestsView/complete.png
Скопируйте нужные ассеты в папку src/assets вашего проекта.
Настройте стили в файле style.css, чтобы получить следующий результат:
Полный код файла style.css
:root {
/* Прозрачный фон */
color-scheme: dark;
background: transparent;
}
@font-face {
font-family: Warhelios;
src: url(./assets/Warhelios-Regular.ttf) format("truetype");
}
body {
/* Устанавливаем размер шрифта в зависимости от ширины окна */
font-size: 1vw;
/* Убираем отступ и скрываем скролл */
margin: 0;
overflow: hidden;
font-family: Warhelios, sans-serif;
}
#app {
display: flex;
gap: 1em;
padding: 1em;
}
.item {
border-radius: 1.3em;
padding: 1.2em;
border: 0.25em solid rgba(255, 173, 65, 0.4);
background:
linear-gradient(0deg, rgba(195, 90, 4, 0.1), rgba(255, 255, 255, 0) 50%),
radial-gradient(farthest-corner at 50% 200%, rgba(251, 166, 20, 0.4), rgba(218, 139, 1, 0.3) 90%);
display: flex;
flex-direction: column;
flex: 1;
max-width: 30%;
box-shadow: 0 0 1em rgba(0, 0, 0, 0.2);
}
.completed {
border: 0.25em solid rgba(106, 255, 65, 0.3);
background:
linear-gradient(0deg, rgba(80, 255, 27, 0.3), rgba(26, 255, 0, 0) 50%),
radial-gradient(farthest-corner at 50% 200%, rgba(4, 102, 5, 0.4), rgba(13, 114, 0, 0.3) 90%);
}
header {
font-size: 1em;
display: flex;
}
header h2 {
flex: 1;
}
header h2,
header p {
font-size: 2.7em;
margin: 0;
font-weight: normal;
}
.current {
color: #ffd28f;
}
img {
width: 9em;
height: 9em;
margin: -3em;
display: none;
}
/* Прогресс бар */
.progress-bar {
height: 0.3em;
width: 100%;
margin-top: 1em;
position: relative;
}
/* Фоновая часть прогресс бара (серые прямоугольники) */
.progress-bar-background {
background-image: url(./assets/pattern_grey.png);
background-repeat: repeat;
background-position: 0 50%;
background-size: 0.4em 1em;
position: absolute;
width: 100%;
height: 100%;
box-shadow: 0 0 2em rgba(0, 0, 0, 0.5);
}
/* Текущее значение прогресса (блик) */
.progress-bar-blink {
height: 5em;
width: 5em;
background-image: url(./assets//glow_small.png);
background-size: contain;
mix-blend-mode: lighten;
position: absolute;
transform: translate(-49%, -50%);
top: 50%;
left: var(--progress, 60%);
transition: left 0.2s ease;
}
/* Заполненная часть прогресс бара */
.progress-bar-pattern {
background-image: url(./assets/pattern_orange.png);
background-repeat: repeat;
background-position: 0 50%;
background-size: 0.4em 1em;
position: absolute;
height: 100%;
width: var(--progress, 60%);
transition: width 0.2s ease;
}
/* Градиентная подсветка прогресс бара */
.progress-bar-pattern::after {
content: "";
position: absolute;
right: 0;
top: 0;
width: 100%;
height: 100%;
background-image: linear-gradient(90deg, rgba(0, 0, 0, 0.6), rgba(255, 206, 122, 0.5));
mix-blend-mode: overlay;
}
/* Переопределяет для завершенных этапов */
.completed .progress-bar-pattern {
background-image: url(./assets/pattern_green.png);
width: 100%;
}
.completed .progress-bar-pattern::after {
background-image: linear-gradient(90deg, rgba(0, 0, 0, 0.6), rgba(187, 255, 78, 0.519), rgba(0, 0, 0, 0.6));
}
.completed .progress-bar-blink,
.completed header p {
display: none;
}
.completed img {
display: block;
}Полный код файла index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>article-pm-widget</title>
</head>
<body>
<div id="app">
<div class="item item-1">
<header>
<h2>VI - VII уровень</h2>
<p><span class="current">18</span> / 25</p>
<img src="./src/assets/complete.png">
</header>
<div class="progress-bar">
<div class="progress-bar-background"></div>
<div class="progress-bar-pattern"></div>
<div class="progress-bar-blink"></div>
</div>
</div>
<div class="item item-2">
<header>
<h2>VII - IX уровень</h2>
<p><span class="current">18</span> / 25</p>
<img src="./src/assets/complete.png">
</header>
<div class="progress-bar">
<div class="progress-bar-background"></div>
<div class="progress-bar-pattern"></div>
<div class="progress-bar-blink"></div>
</div>
</div>
<div class="item item-3 completed">
<header>
<h2>X - XI уровень</h2>
<p><span class="current">18</span> / 25</p>
<img src="./src/assets/complete.png">
</header>
<div class="progress-bar">
<div class="progress-bar-background"></div>
<div class="progress-bar-pattern"></div>
<div class="progress-bar-blink"></div>
</div>
</div>
<script type="module" src="/src/main.js"></script>
</body>
</html>В результате получаем следующий виджет: 
Функционал удалённого управления
Теперь добавим функционал удалённого управления. Для этого установим библиотеку wotstat-widgets-sdk, и объявим в ней удалённые параметры нашего виджета.
В терминале выполните команду:
bun add wotstat-widgets-sdkbun add wotstat-widgets-sdkВ файле src/main.js подключим библиотеку и объявим параметры виджета:
import './style.css'
import { WidgetsRemote } from 'wotstat-widgets-sdk';
const remote = new WidgetsRemote();
remote.defineState('VI - VII уровень', 0, { elementHelper: '.item-1' });
remote.defineState('VII - IX уровень', 0, { elementHelper: '.item-2' });
remote.defineState('X - XI уровень', 0, { elementHelper: '.item-3' });Теперь можем открыть нашу ссылку в панели управления виджетами на сайте ru.widgets.wotstat.info/remote-control и убедиться, что параметры виджета появились в списке, а при наведение мышки обводится нужный блок виджета: 
Обводка работает благодаря свойству elementHelper, в котором мы указали CSS-селектор нужного блока.
Далее нам нужно добавить обработку изменения параметров. Для создадим функцию updateState, и подпишемся на изменения каждого параметра с помощью метода watch:
...
function updateState(selector, value) {
const element = document.querySelector(selector);
// Устанавливаем значение счетчика
element.querySelector('.current').textContent = value;
// Переключаем класс завершенного этапа
if (value >= 25) element.classList.add('completed');
else element.classList.remove('completed');
// Добавляем CSS-переменную для прогресс-бара
element.style.setProperty('--progress', `${100 * value / 25}%`);
}
remote.defineState('VI - VII уровень', 0, { elementHelper: '.item-1' })
.watch((v) => updateState('.item-1', v));
remote.defineState('VII - IX уровень', 0, { elementHelper: '.item-2' })
.watch((v) => updateState('.item-2', v));
remote.defineState('X - XI уровень', 0, { elementHelper: '.item-3' })
.watch((v) => updateState('.item-3', v));Готово! Теперь при изменении параметров в панели управления, виджет будет обновляться автоматически.
Публикация виджета
Остался последний шаг - публикация виджета, что бы он был доступен для использования через интернет.
Для начала нужно скомпилировать проект. В терминале выполните команду:
bun run buildbun run buildВ результате чего будет создана папка dist, в которой будет находиться готовый к публикации сайт.

Этот сайт можно разместить на любом хостинге статических сайтов, например:
- wasmer.io бесплатно после регистрации (можно через Google аккаунт)
- Яндекс Облако в режиме S3 хостига, бесплатно, но регистрация сложная
- GitHub Pages бесплатно, но требует регистрации и создания репозитория на GitHub
Добавление в OBS
После публикации, вставьте ссылку на виджет в панель удалённого управления, сгенерируйте ключ доступа и добавьте виджет в OBS Studio как Browser Source, указав сгенерированную ссылку.
Не забудьте выбрать
Blending Method->SRGB off, подробнее можно изучить руководстве
Исходный код
Весь исходный код виджета доступен в репозитории на GitHub, а результат опубликован в GitHub Pages по ссылке: soprachevak.github.io/mt-pm3-progress-widget и в панели управления ru.widgets.wotstat.info/remote-control
Andrei Soprachev