Skip to content

Tug of War

Tug of War shows a live two-option voting bar in OBS and other tools that support browser sources. Viewers vote by sending activity with a message, such as donations or cheers.

Versus editor
  1. Open the Synchra Dashboard.
  2. Go to Widgets.
  3. Create a Tug of War widget.
  4. Set the option titles, extra keywords, colors and optional images.
  5. Pick Points or Currency display.
  6. Set a start date and optional end date.
  7. Select which message-capable sources should count.
  8. Click Widget URL and add it to OBS as a Browser Source.

Matching is case-insensitive. Symbols are removed and whitespace is collapsed before matching. Empty titles and keywords never match.

Activity types start from the default activity value and can be fine-tuned in the source settings.

You can customize the title, options, starting values, default activity value, display mode, dates, lead amount, countdown, sources, multipliers, colors, font, size, padding, border and visibility settings.

Use custom CSS when the built-in settings are not enough.

CSS order: built-in CSS, Theme CSS, then Widget CSS. Later rules win at equal specificity.

Simplified generated markup; optional elements are marked.

<section
  class="versus-widget"
  data-display-mode="points"
  data-ended="false"
  data-leader="option-a"
>
  <div class="versus-widget__title">Choose a side</div> <!-- Optional -->
  <div class="versus-widget__options"> <!-- Optional -->
    <div class="versus-widget__option" data-option="1">
      <div class="versus-widget__option-title">Option A</div>
      <div class="versus-widget__value">120</div> <!-- Optional -->
    </div>
    <div class="versus-widget__option" data-option="2">
      <div class="versus-widget__option-title">Option B</div>
      <div class="versus-widget__value">95</div> <!-- Optional -->
    </div>
  </div>
  <div class="versus-widget__bar-row">
    <div class="versus-widget__bar-icon">
      <img class="versus-widget__bar-icon-image" alt="Option A" /> <!-- Optional -->
    </div>
    <div class="versus-widget__bar-shell">
      <div class="versus-widget__bar">
        <div class="versus-widget__bar-1"></div>
        <div class="versus-widget__bar-2"></div>
        <div class="versus-widget__divider"></div>
      </div>
      <div class="versus-widget__lead-amount" data-option="1">+25</div> <!-- Optional -->
    </div>
    <div class="versus-widget__bar-icon">
      <img class="versus-widget__bar-icon-image" alt="Option B" /> <!-- Optional -->
    </div>
  </div>
  <div class="versus-widget__meta"> <!-- Optional -->
    <div class="versus-widget__countdown">2 days left</div>
  </div>
</section>

Layout: vertical root; two-column option grid; horizontal icon | live bar | icon row with an overlaid lead badge.

ClassDescription
.versus-widgetRoot container. It owns the widget theme variables.
.versus-widget--disabledAdded when disabled. The built-in CSS hides the widget.
.versus-widget__titleOptional centered widget title.
.versus-widget__optionsOptional two-column wrapper for the option text.
.versus-widget__optionOne option text group. Its data-option identifies the first or second column.
.versus-widget__option-titleOption title. Long words wrap.
.versus-widget__valueOptional option score or amount. Uses tabular numerals.
.versus-widget__bar-rowFlex row containing both icons and the bar.
.versus-widget__bar-iconCircular option icon container. Its border and background use the option color.
.versus-widget__bar-icon-imageOptional image inside an icon container. Uses object-fit: cover.
.versus-widget__bar-shellPositioned wrapper for the bar and optional lead badge.
.versus-widget__barClipped two-part result bar.
.versus-widget__bar-1Option 1 fill.
.versus-widget__bar-2Option 2 fill.
.versus-widget__dividerAnimated divider at the current result split.
.versus-widget__lead-amountOptional badge centered over the bar. Its data-option identifies the leading side.
.versus-widget__metaOptional metadata row below the bar.
.versus-widget__countdownCountdown or ended text.
AttributeElementDescription
data-display-mode="points".versus-widgetValues are formatted as points.
data-display-mode="currency".versus-widgetValues are formatted as currency.
data-ended="false".versus-widgetVoting has not ended.
data-ended="true".versus-widgetVoting has ended.
data-leader="".versus-widgetThe vote is tied or has no leader.
data-leader="<option-id>".versus-widgetContains the user-defined ID of the leading option. Do not assume a fixed ID.
data-option="1".versus-widget__option, .versus-widget__lead-amountFirst option, or a lead badge for the first option.
data-option="2".versus-widget__option, .versus-widget__lead-amountSecond option, or a lead badge for the second option.
VariableValue typeControls
--versus-widget-title-colorCSS colorWidget title color.
--versus-widget-text-colorCSS colorOption title color.
--versus-widget-value-colorCSS colorOption values and lead badge text.
--versus-widget-secondary-text-colorCSS colorCountdown or ended text.
--versus-widget-background-colorCSS colorRoot and lead badge background source.
--versus-widget-border-colorCSS colorRoot border color.
--versus-widget-bar-1-colorCSS color, runtime-ownedFirst option’s configured color. Also useful as a token in custom rules.
--versus-widget-bar-2-colorCSS color, runtime-ownedSecond option’s configured color. Also useful as a token in custom rules.
--versus-widget-bar-1-percentPercentage, runtime-ownedLive width of option 1 and position of the divider. Option 2 uses the remainder.
--versus-widget-border-widthCSS lengthRoot border width.
--versus-widget-border-radiusCSS lengthRoot, bar and badge corner treatment.
--versus-widget-bar-heightCSS lengthResult bar height.
--versus-widget-image-sizeCSS length, runtime-ownedWidth and height of both option icons.
--versus-widget-paddingCSS lengthSpace inside the root.
--versus-widget-font-familyFont family listFont for the entire widget.
--versus-widget-font-sizeCSS lengthBase text size.
--versus-widget-min-widthCSS length or percentageMinimum root width.
--versus-widget-max-widthCSS length or percentageMaximum root width.

Option colors, image size and bar percentage are inline runtime values. Reuse them, but do not override them.

Get a font import, then:

@import url("https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap");

.versus-widget {
  --versus-widget-font-family: "Roboto", sans-serif;
}