<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: chenfanyu</title>
    <description>The latest articles on DEV Community by chenfanyu (@chenfanyu).</description>
    <link>https://dev.to/chenfanyu</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4047444%2F070c35c0-c604-452d-be57-db45e65aaadc.jpg</url>
      <title>DEV Community: chenfanyu</title>
      <link>https://dev.to/chenfanyu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chenfanyu"/>
    <language>en</language>
    <item>
      <title>ZWPlayer: A Free HTML5 Video Player with AI Subtitles, Interactive Annotations, and Vue 2/3 Support</title>
      <dc:creator>chenfanyu</dc:creator>
      <pubDate>Sun, 26 Jul 2026 03:06:05 +0000</pubDate>
      <link>https://dev.to/chenfanyu/zwplayer-a-free-html5-video-player-with-ai-subtitles-interactive-annotations-and-vue-23-support-4hi7</link>
      <guid>https://dev.to/chenfanyu/zwplayer-a-free-html5-video-player-with-ai-subtitles-interactive-annotations-and-vue-23-support-4hi7</guid>
      <description>&lt;p&gt;If you've ever tried to add a serious video player to a web project — one that handles streaming protocols, subtitles, interactivity, and framework integration — you know how painful it can get. That's why I built &lt;strong&gt;ZWPlayer&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this post, I'll walk you through what ZWPlayer is, why it exists, what it can do, and how to integrate it into your project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4z8sgya0b5b8vmjh507y.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4z8sgya0b5b8vmjh507y.jpg" alt="zwplayer-subtitle-audiotrack" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is ZWPlayer?
&lt;/h2&gt;

&lt;p&gt;ZWPlayer is a free, full-featured HTML5 video player designed for modern web applications. The entire core is &lt;strong&gt;a single JavaScript file with zero external dependencies&lt;/strong&gt; — no jQuery, no third-party CDN required. It works in any environment, including air-gapped intranets.&lt;/p&gt;

&lt;p&gt;It comes with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Native wrapper components for &lt;strong&gt;Vue 2&lt;/strong&gt;, &lt;strong&gt;Vue 3&lt;/strong&gt;, and &lt;strong&gt;React&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;WordPress plugin&lt;/strong&gt; (Gutenberg block)&lt;/li&gt;
&lt;li&gt;A suite of &lt;strong&gt;free online visual editors&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI-powered&lt;/strong&gt; subtitle translation, speech recognition, and voice synthesis&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🌐 Website: &lt;a href="https://www.zwplayer.com" rel="noopener noreferrer"&gt;https://www.zwplayer.com&lt;/a&gt;&lt;br&gt;
🎮 Live demo: &lt;a href="https://www.zwplayer.com/tools/videoplayer/" rel="noopener noreferrer"&gt;https://www.zwplayer.com/tools/videoplayer/&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Why I Built It
&lt;/h2&gt;

&lt;p&gt;A few years ago, I needed a web video player for a project. The requirements were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Support multiple streaming protocols (HLS, DASH, FLV, WebRTC)&lt;/li&gt;
&lt;li&gt;Built-in danmaku (bullet comments) — a must for the Chinese market&lt;/li&gt;
&lt;li&gt;Multi-track subtitle support with search and translation&lt;/li&gt;
&lt;li&gt;Interactive video capabilities (quizzes, hotspots, forms)&lt;/li&gt;
&lt;li&gt;Easy integration with Vue.js&lt;/li&gt;
&lt;li&gt;Free&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I evaluated the existing options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Video.js&lt;/strong&gt;: Good protocol support, but no built-in danmaku, basic subtitles, no interactive features, and relies on community plugins for Vue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plyr&lt;/strong&gt;: Clean UI, but limited streaming protocols, no danmaku, basic subtitles, and no official Vue support.
...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nothing checked all the boxes. So I started building my own — and it grew into something much bigger than I originally planned.&lt;/p&gt;


&lt;h2&gt;
  
  
  Streaming Protocol Support
&lt;/h2&gt;

&lt;p&gt;ZWPlayer handles virtually every streaming protocol you'll encounter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MP4 / WebM / OGG&lt;/strong&gt; — Native browser playback&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HLS&lt;/strong&gt; (.m3u8) — HTTP Live Streaming via hls.js&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DASH&lt;/strong&gt; (.mpd) — Dynamic Adaptive Streaming via dash.js&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP-FLV&lt;/strong&gt; — Low-latency streaming via flv.js&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MPEG-TS&lt;/strong&gt; — Transport stream via mpegts.js&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebRTC&lt;/strong&gt; — Real-time streaming&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RTSP&lt;/strong&gt; — Play RTSP streams directly in the browser through a media gateway, no plugins needed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Protocol plugins are &lt;strong&gt;loaded on demand at runtime&lt;/strong&gt; — they're never bundled into your application. This keeps your bundle lean and the player's initial load fast.&lt;/p&gt;


&lt;h2&gt;
  
  
  🤖 AI-Powered Features
&lt;/h2&gt;

&lt;p&gt;This is where things get exciting. ZWPlayer integrates AI capabilities directly into the playback experience:&lt;/p&gt;
&lt;h3&gt;
  
  
  Real-Time Subtitle Translation
&lt;/h3&gt;

&lt;p&gt;Connect a translation API and subtitles are translated on-the-fly during playback. The translated text appears as a secondary subtitle track below the original. Supports &lt;strong&gt;13 languages&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  ASR — Automatic Speech Recognition
&lt;/h3&gt;

&lt;p&gt;No subtitle file? No problem. ZWPlayer can leverage AI speech recognition to &lt;strong&gt;automatically extract text from video audio and generate subtitles&lt;/strong&gt;. No manual transcription needed.&lt;/p&gt;
&lt;h3&gt;
  
  
  TTS — Text-to-Speech Synthesis
&lt;/h3&gt;

&lt;p&gt;Generate spoken narration from subtitle text using AI voice synthesis. Useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding voiceovers to foreign-language content&lt;/li&gt;
&lt;li&gt;Accessibility (screen reader alternative)&lt;/li&gt;
&lt;li&gt;Dubbing and localization workflows&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  🎯 Interactive Annotation System
&lt;/h2&gt;

&lt;p&gt;This is the feature I'm most proud of. ZWPlayer lets you overlay &lt;strong&gt;13 types of interactive components&lt;/strong&gt; on the video timeline:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hotspot&lt;/strong&gt;: Clickable regions on the video&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Button&lt;/strong&gt;: Action triggers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Text&lt;/strong&gt;: Overlay text messages&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Image&lt;/strong&gt;: Display images at specific times&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choice&lt;/strong&gt;: Multiple-choice questions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quiz&lt;/strong&gt;: Knowledge-check questions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Form&lt;/strong&gt;: Collect user input&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Poll&lt;/strong&gt;: Audience voting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Card&lt;/strong&gt;: Information cards&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Webview&lt;/strong&gt;: Embed external web pages&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Map&lt;/strong&gt;: Display interactive maps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Countdown&lt;/strong&gt;: Timer overlays&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Speed Controller&lt;/strong&gt;: Playback speed adjustment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use cases:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;E-learning&lt;/strong&gt;: Pause the video at a key concept, present a quiz, and only continue if the answer is correct&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Corporate training&lt;/strong&gt;: Compliance checks with mandatory completion&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Marketing&lt;/strong&gt;: Interactive product demos with clickable hotspots&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All annotations support &lt;strong&gt;18 entrance/emphasis/exit animations&lt;/strong&gt; and session variables for conditional logic.&lt;/p&gt;
&lt;h3&gt;
  
  
  Visual Annotation Editor
&lt;/h3&gt;

&lt;p&gt;You don't need to write JSON by hand. ZWPlayer includes a &lt;strong&gt;drag-and-drop visual editor&lt;/strong&gt; where you can place interactive components on the video timeline, configure their behavior, and export the result as JSON:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://www.zwplayer.com/tools/annotation/" rel="noopener noreferrer"&gt;Try the Annotation Editor&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Vue 2 &amp;amp; Vue 3 Integration
&lt;/h2&gt;

&lt;p&gt;ZWPlayer provides dedicated npm packages for both Vue 2 and Vue 3. The API is &lt;strong&gt;identical across both versions&lt;/strong&gt; — same props, same events, same methods.&lt;/p&gt;
&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Vue 3&lt;/span&gt;
npm &lt;span class="nb"&gt;install &lt;/span&gt;zwplayervue3 &lt;span class="nt"&gt;--save&lt;/span&gt;

&lt;span class="c"&gt;# Vue 2&lt;/span&gt;
npm &lt;span class="nb"&gt;install &lt;/span&gt;zwplayer-vue2x &lt;span class="nt"&gt;--save&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Vue 3 Example (Composition API)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;zwplayer&lt;/span&gt;
    &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"playerOpen"&lt;/span&gt;
    &lt;span class="na"&gt;ref=&lt;/span&gt;&lt;span class="s"&gt;"zwplayerRef"&lt;/span&gt;
    &lt;span class="na"&gt;:murl=&lt;/span&gt;&lt;span class="s"&gt;"movieUrl"&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;onready=&lt;/span&gt;&lt;span class="s"&gt;"onPlayerReady"&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;onmediaevent=&lt;/span&gt;&lt;span class="s"&gt;"onPlayerMediaEvent"&lt;/span&gt;
    &lt;span class="na"&gt;:autoplay=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;
    &lt;span class="na"&gt;:fluid=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;
    &lt;span class="na"&gt;:enableDanmu=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;
    &lt;span class="na"&gt;:snapshotButton=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;
    &lt;span class="na"&gt;:chapterButton=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;
  &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;zwplayer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zwplayervue3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;movieUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://your-video-url.mp4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;playerOpen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;zwplayerRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onPlayerReady&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Player is ready!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;zwplayerRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;

  &lt;span class="c1"&gt;// Set chapters&lt;/span&gt;
  &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setChapters&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Introduction&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Main Content&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Summary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onPlayerMediaEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Media event:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Vue 2 Example (Options API)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;zwplayer&lt;/span&gt;
    &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"playerOpen"&lt;/span&gt;
    &lt;span class="na"&gt;ref=&lt;/span&gt;&lt;span class="s"&gt;"zwplayerRef"&lt;/span&gt;
    &lt;span class="na"&gt;:murl=&lt;/span&gt;&lt;span class="s"&gt;"movieUrl"&lt;/span&gt;
    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;onready=&lt;/span&gt;&lt;span class="s"&gt;"onPlayerReady"&lt;/span&gt;
    &lt;span class="na"&gt;:autoplay=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;
    &lt;span class="na"&gt;:fluid=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;
  &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;zwplayer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zwplayer-vue2x&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;zwplayer&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;movieUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://your-video-url.mp4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;playerOpen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;onPlayerReady&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$refs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zwplayerRef&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Player is ready!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Key Differences Between Vue 2 and Vue 3 Packages
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Package name&lt;/strong&gt;: &lt;code&gt;zwplayer-vue2x&lt;/code&gt; (Vue 2) vs &lt;code&gt;zwplayervue3&lt;/code&gt; (Vue 3)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Registration&lt;/strong&gt;: Local / Global (Vue 2) vs Local / Global / &lt;code&gt;app.use()&lt;/code&gt; (Vue 3)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composition API&lt;/strong&gt;: ❌ (Vue 2) vs ✅ Full &lt;code&gt;&amp;lt;script setup&amp;gt;&lt;/code&gt; (Vue 3)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Style scoping&lt;/strong&gt;: &lt;code&gt;/deep/&lt;/code&gt; or &lt;code&gt;::v-deep&lt;/code&gt; (Vue 2) vs &lt;code&gt;:deep()&lt;/code&gt; (Vue 3)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build tools&lt;/strong&gt;: Vue CLI / Webpack (Vue 2) vs Vite or Webpack (Vue 3)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Props, events, and methods are identical.&lt;/strong&gt; Migrating from Vue 2 to Vue 3 requires only changing the import — everything else stays the same.&lt;/p&gt;
&lt;h3&gt;
  
  
  Architecture: Dynamic Loading
&lt;/h3&gt;

&lt;p&gt;One design decision worth highlighting: the Vue wrapper packages &lt;strong&gt;do not bundle the player core into your Vue build output&lt;/strong&gt;. Instead, the core library is placed in your &lt;code&gt;public/zwplayer&lt;/code&gt; directory and loaded dynamically at runtime.&lt;/p&gt;

&lt;p&gt;This gives you three advantages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Smaller bundle size&lt;/strong&gt; — your Vue app stays lean&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Seamless upgrades&lt;/strong&gt; — just replace the &lt;code&gt;zwplayer.js&lt;/code&gt; file, no rebuild needed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CDN flexibility&lt;/strong&gt; — point to a custom CDN via the &lt;code&gt;zwplayerlib&lt;/code&gt; prop&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  More Features
&lt;/h2&gt;
&lt;h3&gt;
  
  
  💬 Danmaku (Bullet Comments)
&lt;/h3&gt;

&lt;p&gt;Built-in danmaku engine. Send danmaku directly from your Vue component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$refs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zwplayerRef&lt;/span&gt;
&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendDanmu&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Great video!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#ff6b6b&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1px solid #ccc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  📝 Multi-Track Subtitles
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Supports SRT, WebVTT, and BCC formats&lt;/li&gt;
&lt;li&gt;Load multiple subtitle tracks and assign primary/secondary display&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full-text subtitle search&lt;/strong&gt; — find any line in the transcript instantly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A-B loop&lt;/strong&gt; — select a subtitle line and loop that segment for language learning practice&lt;/li&gt;
&lt;li&gt;Export individual tracks as SRT files&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🔒 Anti-Piracy Watermark
&lt;/h3&gt;

&lt;p&gt;Three modes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Static&lt;/strong&gt; — fixed position&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic&lt;/strong&gt; — bounces around the video frame (anti-screen-recording)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tiled&lt;/strong&gt; — covers the entire frame&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Supports template variables: &lt;code&gt;{user_name}&lt;/code&gt;, &lt;code&gt;{ip}&lt;/code&gt;, &lt;code&gt;{time}&lt;/code&gt; — so even if someone records the screen, you can trace it back to the viewer.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://www.zwplayer.com/tools/watermark/" rel="noopener noreferrer"&gt;Try the Watermark Editor&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  📑 Chapters &amp;amp; Thumbnails
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;JSON-based chapter markers with visual indicators on the progress bar&lt;/li&gt;
&lt;li&gt;Thumbnail sprite sheet preview on hover&lt;/li&gt;
&lt;li&gt;Both have dedicated visual editors&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  📋 Playlists
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Multi-level grouping structure&lt;/li&gt;
&lt;li&gt;Each video can have its own subtitles, chapters, watermarks, and annotations&lt;/li&gt;
&lt;li&gt;Automatic progress memory&lt;/li&gt;
&lt;li&gt;Continuous playback with error auto-skip&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  And More...
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;📸 Screenshots&lt;/li&gt;
&lt;li&gt;🔍 Canvas magnifier (press Z to toggle, 1.5x–4x zoom)&lt;/li&gt;
&lt;li&gt;📺 Casting (Google Cast + AirPlay)&lt;/li&gt;
&lt;li&gt;🖥️ Picture-in-Picture&lt;/li&gt;
&lt;li&gt;🎙️ Screen recording&lt;/li&gt;
&lt;li&gt;🔊 Volume boost&lt;/li&gt;
&lt;li&gt;📱 iOS web fullscreen with safe-area support&lt;/li&gt;
&lt;li&gt;📂 Local file drag-and-drop playback&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Free Online Visual Tools
&lt;/h2&gt;

&lt;p&gt;I built a suite of visual editors that output JSON the player consumes directly. All free, no registration required:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Annotation Editor&lt;/strong&gt;: Drag-and-drop interactive components - &lt;a href="https://www.zwplayer.com/tools/annotation/" rel="noopener noreferrer"&gt;Open&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Watermark Editor&lt;/strong&gt;: Configure watermark visually - &lt;a href="https://www.zwplayer.com/tools/watermark/" rel="noopener noreferrer"&gt;Open&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subtitle Editor&lt;/strong&gt;: Edit and translate subtitles - &lt;a href="https://www.zwplayer.com/tools/subtitle/" rel="noopener noreferrer"&gt;Open&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chapter Editor&lt;/strong&gt;: Mark chapter points - &lt;a href="https://www.zwplayer.com/tools/chapter/" rel="noopener noreferrer"&gt;Open&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Thumbnail Generator&lt;/strong&gt;: Generate thumbnail sprite sheets - &lt;a href="https://www.zwplayer.com/tools/thumbnail/" rel="noopener noreferrer"&gt;Open&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Playlist Editor&lt;/strong&gt;: Manage video playlists - &lt;a href="https://www.zwplayer.com/tools/playlist/" rel="noopener noreferrer"&gt;Open&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All tools export data in the &lt;strong&gt;ZWMAP&lt;/strong&gt; unified JSON protocol. The player auto-detects the data type — just pass the JSON and it works.&lt;/p&gt;




&lt;h2&gt;
  
  
  ZWMAP: Unified Data Protocol
&lt;/h2&gt;

&lt;p&gt;In v3.3.0, I introduced &lt;strong&gt;ZWMAP&lt;/strong&gt; — a unified JSON protocol that standardizes the data format across all six modules: thumbnails, chapters, subtitles, watermarks, annotations, and playlists.&lt;/p&gt;

&lt;p&gt;Every visual editor exports data in this format. The player automatically identifies the type and applies it. This means you can manage all your video metadata in one consistent schema.&lt;/p&gt;




&lt;h2&gt;
  
  
  Links &amp;amp; Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Website&lt;/strong&gt;: &lt;a href="https://www.zwplayer.com" rel="noopener noreferrer"&gt;https://www.zwplayer.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live Demo&lt;/strong&gt;: &lt;a href="https://www.zwplayer.com/tools/videoplayer/" rel="noopener noreferrer"&gt;https://www.zwplayer.com/tools/videoplayer/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation&lt;/strong&gt;: &lt;a href="https://www.zwplayer.com/docs/support.html" rel="noopener noreferrer"&gt;https://www.zwplayer.com/docs/support.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vue 3 NPM&lt;/strong&gt;: &lt;a href="https://www.npmjs.com/package/zwplayervue3" rel="noopener noreferrer"&gt;zwplayervue3&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vue 2 NPM&lt;/strong&gt;: &lt;a href="https://www.npmjs.com/package/zwplayer-vue2x" rel="noopener noreferrer"&gt;zwplayer-vue2x&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;React NPM&lt;/strong&gt;: &lt;a href="https://www.npmjs.com/package/zwplayer-react" rel="noopener noreferrer"&gt;zwplayer-react&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vue 3 Demo Repo&lt;/strong&gt;: &lt;a href="https://github.com/chenfanyu/zwplayervue3-demo" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vue 2 Demo Repo&lt;/strong&gt;: &lt;a href="https://github.com/chenfanyu/zwplayer-vue2x-demo" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Releases&lt;/strong&gt;: &lt;a href="https://github.com/chenfanyu/zwplayer-release/releases" rel="noopener noreferrer"&gt;Releases&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WordPress Plugin&lt;/strong&gt;: &lt;a href="https://wordpress.org/plugins/zw-player-video-embed/" rel="noopener noreferrer"&gt;wordpress.org&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;ZWPlayer started as a personal itch — I needed a video player that could do more than just play video. Over the years it has grown into a complete interactive video ecosystem with AI capabilities, a unified data protocol, visual editing tools, and framework-native integrations.&lt;/p&gt;

&lt;p&gt;It's completely &lt;strong&gt;free to use&lt;/strong&gt;. No ads, no tracking, no telemetry. Works in air-gapped environments.&lt;/p&gt;

&lt;p&gt;If you're building anything with video on the web — whether it's an e-learning platform, a corporate training system, a media site, or just a side project — I'd love for you to give it a try.&lt;/p&gt;

&lt;p&gt;Feedback, suggestions, and feature requests are all welcome. Feel free to leave a comment or reach out!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>vue</category>
      <category>webdev</category>
      <category>video</category>
    </item>
  </channel>
</rss>
