<?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: Vladimir Grigoryan</title>
    <description>The latest articles on DEV Community by Vladimir Grigoryan (@vlad_grigoryan).</description>
    <link>https://dev.to/vlad_grigoryan</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%2F4196264%2F404f5c4c-3859-4a6e-bee8-d8c5d213dd27.png</url>
      <title>DEV Community: Vladimir Grigoryan</title>
      <link>https://dev.to/vlad_grigoryan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vlad_grigoryan"/>
    <language>en</language>
    <item>
      <title>Running Kokoro in WordPress: generate in the admin tab, serve a saved file</title>
      <dc:creator>Vladimir Grigoryan</dc:creator>
      <pubDate>Wed, 21 Oct 2026 09:00:25 +0000</pubDate>
      <link>https://dev.to/vlad_grigoryan/running-kokoro-in-wordpress-generate-in-the-admin-tab-serve-a-saved-file-42hm</link>
      <guid>https://dev.to/vlad_grigoryan/running-kokoro-in-wordpress-generate-in-the-admin-tab-serve-a-saved-file-42hm</guid>
      <description>&lt;p&gt;A browser TTS demo usually ends when the audio starts playing. For a WordPress article, that's only part of the job. The recording also needs to survive the tab closing, stay attached to the right post, and keep working for readers on phones.&lt;/p&gt;

&lt;p&gt;The approach I use is to generate the audio in the site owner's admin browser and save a file in WordPress. Readers then play that file. They never need to load the speech model.&lt;/p&gt;

&lt;p&gt;The flow looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Saved post
  -&amp;gt; text prepared for speech
  -&amp;gt; Kokoro in the admin browser
  -&amp;gt; audio samples
  -&amp;gt; encode an audio file
  -&amp;gt; upload to WordPress
  -&amp;gt; Media Library attachment
  -&amp;gt; article player
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kokoro produces audio; MP3 encoding is a separate step. The kokoro-js examples save WAV files. If your publishing workflow needs MP3, include an encoder rather than changing the filename extension.&lt;/p&gt;

&lt;p&gt;There are three things I'd decide before wiring up the player.&lt;/p&gt;

&lt;p&gt;First, what exactly gets read? A post can contain headings, captions, code, tables, and embeds. Stripping all the HTML won't tell you whether a table makes sense when spoken. Show the prepared text or a report of skipped sections, then listen to a representative post before recording an archive. Names and abbreviations deserve a check too.&lt;/p&gt;

&lt;p&gt;Second, where does the work happen? Keep inference in the admin screen. WordPress already handles the post, the user's permissions, and media storage. It doesn't need to run the speech model in this design. But this also means recording depends on the owner's machine and the browser tab staying open. It isn't an overnight server job.&lt;/p&gt;

&lt;p&gt;The kokoro-js README documents WebAssembly and WebGPU backends and recommends fp32 for WebGPU. Test the path you actually ship on the hardware you expect people to use. A short sample can tell you whether generation works; an estimate extrapolated from it isn't a measured recording time for a long article.&lt;/p&gt;

&lt;p&gt;Third, what happens after an edit? An attachment ID alone doesn't tell you whether the recording matches the current post. I'd keep a record shaped something like this, with illustrative values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"post_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"attachment_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;456&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"spoken_text_hash"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hash-of-the-prepared-text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"voice"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"af_heart"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"backend"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"webgpu"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dtype"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fp32"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hash the text you actually send to the model. When that text changes, mark the recording as out of date. Keep the previous audio available until its replacement has been uploaded successfully. A failed upload shouldn't remove the version readers can already play.&lt;/p&gt;

&lt;p&gt;WordPress's Media REST API supports creating an attachment and associating it with a post. An admin integration also needs authenticated requests and permission checks. Once the file is stored, the public page can use an ordinary audio player. Keep the inference runtime out of that page's bundle.&lt;/p&gt;

&lt;p&gt;There's also a privacy detail worth spelling out: local speech generation means the text can be processed without sending it to a TTS provider. Your WordPress host still stores the post, receives the recording, and serves it to readers. The initial model download needs a connection, and a browser cache can be cleared.&lt;/p&gt;

&lt;p&gt;Before calling the workflow finished, I'd test a short post, a post with a table or code block, an interrupted recording, a failed upload, and an edit after recording. Then open the published article on a different device. That checks the handoff from generation to playback, which a browser demo by itself doesn't cover.&lt;/p&gt;

&lt;p&gt;References: &lt;a href="https://github.com/hexgrad/kokoro/tree/main/kokoro.js" rel="noopener noreferrer"&gt;kokoro-js usage and backends&lt;/a&gt;, &lt;a href="https://developer.wordpress.org/rest-api/reference/media/" rel="noopener noreferrer"&gt;WordPress Media REST API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I used AI assistance to draft and edit this article.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>javascript</category>
      <category>architecture</category>
    </item>
    <item>
      <title>How to record a browser demo without exposing API keys</title>
      <dc:creator>Vladimir Grigoryan</dc:creator>
      <pubDate>Tue, 20 Oct 2026 14:03:06 +0000</pubDate>
      <link>https://dev.to/vlad_grigoryan/how-to-record-a-browser-demo-without-exposing-api-keys-2bmg</link>
      <guid>https://dev.to/vlad_grigoryan/how-to-record-a-browser-demo-without-exposing-api-keys-2bmg</guid>
      <description>&lt;p&gt;A demo often passes through a settings page on the way to the feature you want to explain. That page might contain a deploy token, an account email or a customer record. The audience doesn't need those details, but the recorder will capture them anyway.&lt;/p&gt;

&lt;p&gt;Preparing the account before recording makes the final review much easier. Here's a practical workflow for a browser walkthrough.&lt;/p&gt;

&lt;p&gt;We make Nixel, a Chrome extension for covering sensitive text on web pages. I'll use it as an example below, including where it needs a manual check.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with fictional values
&lt;/h2&gt;

&lt;p&gt;Use a separate demo account when you can. Give it sample records and remove unrelated conversations, billing details and customer data.&lt;/p&gt;

&lt;p&gt;If the walkthrough only needs to explain the shape of a request, use an unmistakably fictional value such as YOUR_DEMO_API_KEY. Don't paste a working credential into the example just to make the screen look realistic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Walk through the route before pressing Record
&lt;/h2&gt;

&lt;p&gt;Open each page, menu and expanded panel you plan to show. Check the settings screen, account menu, notification area and any place where you copy a request or response.&lt;/p&gt;

&lt;p&gt;Include the transitions in this rehearsal. A value that is covered on the final screen may have been visible for a moment while a panel opened.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prepare the view you will capture
&lt;/h2&gt;

&lt;p&gt;Choose the browser tab or window needed for the demo. Close unrelated tabs and panels. If the walkthrough moves into a terminal, desktop editor or another application, prepare that view separately.&lt;/p&gt;

&lt;p&gt;For credentials that must remain on the page, use an opaque cover and check that it spans the whole value. Inspect the first and last characters, and look for repeated copies elsewhere on the page. Treat any automatic detector's findings as a starting point for that review.&lt;/p&gt;

&lt;h2&gt;
  
  
  An example with Nixel
&lt;/h2&gt;

&lt;p&gt;Open Nixel on the Chrome page you plan to record. Review the supported API keys, tokens and other sensitive text it detects, then choose solid bars or labels for the values that need to stay hidden. Detection runs on your device.&lt;/p&gt;

&lt;p&gt;Check the page again after opening menus or moving to another screen. Nixel can miss values and doesn't cover your whole desktop, other apps or browser controls. Don't rely on it to detect text inside images or videos.&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%2F0laqrlel0gsvynzn8zsh.png" 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%2F0laqrlel0gsvynzn8zsh.png" alt="A fictional dashboard before and after Nixel covers detected sensitive text" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The before-and-after screenshot uses a fictional dashboard, so none of the example credentials are real.&lt;/p&gt;

&lt;p&gt;If you want to try the same workflow, Nixel is available here:&lt;br&gt;
&lt;a href="https://chromewebstore.google.com/detail/nixel/mhdpmmbcnoakhpaemgpnomjpgklbdapk" rel="noopener noreferrer"&gt;https://chromewebstore.google.com/detail/nixel/mhdpmmbcnoakhpaemgpnomjpgklbdapk&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Record a short test
&lt;/h2&gt;

&lt;p&gt;Make a short recording that includes opening settings, expanding a panel and switching between the pages you need. Play the recorded file back at a readable size.&lt;/p&gt;

&lt;p&gt;Check what the recorder actually captured, including the edges of the window and any account menus. This is also a chance to spot an awkward crop or a browser control you didn't mean to show.&lt;/p&gt;

&lt;h2&gt;
  
  
  Review the exported file
&lt;/h2&gt;

&lt;p&gt;After the full take, watch the exact exported video you intend to upload. Check the moments when pages load, settings open and recording stops. Pause where a sensitive value might have appeared.&lt;/p&gt;

&lt;p&gt;If you find an exposed credential, remove that section or record it again before sharing. Check that the file you upload is the reviewed export, rather than an earlier take.&lt;/p&gt;

&lt;h2&gt;
  
  
  If a working key was already shared
&lt;/h2&gt;

&lt;p&gt;Editing the video doesn't revoke the key. Follow the credential provider's response steps. For example, AWS recommends replacing access keys that may be compromised and deleting the previous keys:&lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/securing_access-keys.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/IAM/latest/UserGuide/securing_access-keys.html&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Before uploading
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;The account uses fictional data wherever possible.&lt;/li&gt;
&lt;li&gt;Every real credential in the prepared view is covered or removed.&lt;/li&gt;
&lt;li&gt;Menus, transitions and other applications have been checked.&lt;/li&gt;
&lt;li&gt;The file being uploaded is the export you reviewed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Disclosure: AI assisted with drafting this article.&lt;/p&gt;

</description>
      <category>security</category>
      <category>privacy</category>
    </item>
  </channel>
</rss>
