<?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: Yuichiro Tachibana (Tsuchiya)</title>
    <description>The latest articles on DEV Community by Yuichiro Tachibana (Tsuchiya) (@whitphx).</description>
    <link>https://dev.to/whitphx</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F347706%2F96fa6ef1-7090-468d-ab36-353bcd1e24ee.jpg</url>
      <title>DEV Community: Yuichiro Tachibana (Tsuchiya)</title>
      <link>https://dev.to/whitphx</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/whitphx"/>
    <language>en</language>
    <item>
      <title>Limit concurrent execution of compute-intensive code in a Streamlit app</title>
      <dc:creator>Yuichiro Tachibana (Tsuchiya)</dc:creator>
      <pubDate>Tue, 27 Feb 2024 08:44:09 +0000</pubDate>
      <link>https://dev.to/whitphx/limit-concurrent-execution-of-compute-intensive-code-in-a-streamlit-app-239d</link>
      <guid>https://dev.to/whitphx/limit-concurrent-execution-of-compute-intensive-code-in-a-streamlit-app-239d</guid>
      <description>&lt;p&gt;&lt;em&gt;Co-written and improved by &lt;a href="https://twitter.com/multimodalart"&gt;@multimodalart&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: &lt;a href="https://arnaudmiribel.github.io/streamlit-extras/extras/concurrency_limiter/"&gt;&lt;code&gt;concurrency_limiter&lt;/code&gt; in &lt;code&gt;streamlit-extras&lt;/code&gt;&lt;/a&gt; is now available for this purpose.&lt;/p&gt;

&lt;p&gt;When you are developing a Streamlit app that includes a compute- or resource-intensive task, such as machine learning inference using a GPU,&lt;br&gt;
you may want to ensure that such a piece of the code only runs in a single thread (session) at a time, even when multiple users are accessing the app at the same time.&lt;/p&gt;

&lt;p&gt;In such a case, using a &lt;strong&gt;lock&lt;/strong&gt; is a solution.&lt;br&gt;
Streamlit creates a separate thread for each user session, so you can use a lock shared among the threads to control the execution of the compute-intensive code.&lt;/p&gt;

&lt;p&gt;(For Gradio users: &lt;em&gt;queue&lt;/em&gt; is a well-known solution for this problem in the case of Gradio.)&lt;/p&gt;

&lt;p&gt;Here is an example of how to use a lock in a Streamlit app.&lt;br&gt;
&lt;code&gt;st.cache_resource&lt;/code&gt; is used to create a global lock that is shared among the threads,&lt;br&gt;
and the lock is used to ensure that &lt;code&gt;compute_intensive_task()&lt;/code&gt; is executed by only one thread at a time (if &lt;code&gt;compute_intensive_task()&lt;/code&gt; is defined in another module, see the last section).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;streamlit&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;threading&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;compute_intensive_task&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; Your compute-intensive code here &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="nd"&gt;@st.cache_resource&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_global_lock&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;threading&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="n"&gt;global_lock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_global_lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spinner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Running a compute-intensive task&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;global_lock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Task started&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;compute_intensive_task&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Task completed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that, with this lock-based approach, the concurrency of the locked task is limited to 1 per lock, while the queue and worker pattern can be used to control the concurrency to any number.&lt;/p&gt;

&lt;p&gt;Practically, it would be a good idea to add a button to manually trigger the compute-intensive task in such cases. Without it, the task would be executed every time a new user accesses the app or the app is reloaded, which is not efficient.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;streamlit&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;threading&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;compute_intensive_task&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; Your compute-intensive code here &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="nd"&gt;@st.cache_resource&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_global_lock&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;threading&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="n"&gt;global_lock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_global_lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Run a compute-intensive task&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spinner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Running a compute-intensive task&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;global_lock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Task started&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;compute_intensive_task&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Task completed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want something like a "concurrency group", you can create multiple locks and use them in the same way as below. To do so, &lt;code&gt;get_global_lock()&lt;/code&gt; is changed to take a &lt;code&gt;key&lt;/code&gt; argument so it returns different locks for different keys, as the &lt;code&gt;st.cache_resource&lt;/code&gt; decorator controls the cache with the func arguments (and the func's source code).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;streamlit&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;threading&lt;/span&gt;


&lt;span class="nd"&gt;@st.cache_resource&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_global_lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;threading&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="n"&gt;global_lock_A&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_global_lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spinner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Running a compute-intensive task A&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;global_lock_A&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Task A started&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;compute_intensive_task_A&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;global_lock_B&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_global_lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;B&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spinner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Running a compute-intensive task B&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;global_lock_B&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Task B started&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;compute_intensive_task_B&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Task completed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the task is defined in another module and the Streamlit app script imports it, you can define the lock object in the module scope and simply use it in the function. This lock object will be shared among the threads.&lt;br&gt;
This is because, while Streamlit runs the main app script in a separate namespace in a different thread for each user session, the imported modules are loaded only once and shared among the threads like normal Python module usage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app.py (main Streamlit app script)
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;streamlit&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;compute_intensive_task&lt;/span&gt;


&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spinner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Running a compute-intensive task&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;compute_intensive_task&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Task completed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# foo.py (module containing the compute-intensive task)
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;threading&lt;/span&gt;


&lt;span class="n"&gt;lock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;threading&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;compute_intensive_task&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; Your compute-intensive code here &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>Are multiple submissions to PyCon (or tech conferences) OK?</title>
      <dc:creator>Yuichiro Tachibana (Tsuchiya)</dc:creator>
      <pubDate>Tue, 24 Oct 2023 15:04:52 +0000</pubDate>
      <link>https://dev.to/whitphx/are-multiple-submissions-to-pycon-or-tech-conferences-ok-4n4g</link>
      <guid>https://dev.to/whitphx/are-multiple-submissions-to-pycon-or-tech-conferences-ok-4n4g</guid>
      <description>&lt;p&gt;&lt;strong&gt;YES✅&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Different from academia
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Unlike academic conferences and journals&lt;/strong&gt; , it is common to submit the same talk proposal to multiple PyCon held in different countries/places &lt;strong&gt;simultaneously&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For other tech conferences, I have neither participated in nor submitted a talk proposal, so I don’t know the situation. You should check the conference’s website or ask the organizer or experienced people around you.&lt;/p&gt;

&lt;h2&gt;
  
  
  My case
&lt;/h2&gt;

&lt;p&gt;In 2022, I presented a talk titled “Real-time browser-ready computer vision apps with Streamlit” at &lt;a href="https://ep2022.europython.eu/"&gt;EuroPython 2022&lt;/a&gt; and &lt;a href="https://tw.pycon.org/2022/"&gt;PyCon APAC 2022&lt;/a&gt;. This time, I first submitted the talk proposal to &lt;a href="https://us.pycon.org/2022/"&gt;PyCon US 2022&lt;/a&gt;, then before the result came out, I submitted the same talk proposal to EuroPython and PyCon APAC as well. Because it was my first time to submit a talk proposal to PyCon, I was not sure if it was OK to submit the same talk proposal to multiple PyCon, so I asked the EuroPython and PyCon APAC organizers via e-mails. &lt;strong&gt;Then they said it was OK&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;After I entered the PyCon sphere, I found that &lt;strong&gt;it is actually common&lt;/strong&gt;. Many people do this, and it is sometimes a good strategy for them to make travel to many countries! (See my related post👉 &lt;a href="https://www.whitphx.info/posts/20230511-conference-driven-travels/"&gt;Conf-driven Traveling✈️&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I wrote this post because I couldn’t find any information clearly answering this question on the Internet.&lt;/p&gt;

&lt;p&gt;In 2023, I actually submitted the same talk proposal to multiple PyCon and it was accepted by &lt;a href="https://2023.pycon.de/program/GYEZVW/"&gt;PyCon DE &amp;amp; PyData Berlin 2023&lt;/a&gt;, &lt;a href="https://pretalx.com/pycon-lt-2023/talk/3BQCJW/"&gt;PyCon LT&lt;/a&gt;, &lt;a href="https://tw.pycon.org/2023/en-us/conference/talk/301"&gt;PyCon TW&lt;/a&gt;, and &lt;a href="https://2023-apac.pycon.jp/timetable?id=YTGKPT"&gt;PyCon APAC&lt;/a&gt;🎉&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Find VSCode's undocumented when clause contexts</title>
      <dc:creator>Yuichiro Tachibana (Tsuchiya)</dc:creator>
      <pubDate>Thu, 16 Feb 2023 16:09:41 +0000</pubDate>
      <link>https://dev.to/whitphx/find-vscodes-undocumented-when-clause-contexts-1p2g</link>
      <guid>https://dev.to/whitphx/find-vscodes-undocumented-when-clause-contexts-1p2g</guid>
      <description>&lt;h2&gt;
  
  
  Command to get the VSCode's when clause contexts
&lt;/h2&gt;

&lt;p&gt;VSCode has many undocumented &lt;a href="https://code.visualstudio.com/api/references/when-clause-contexts"&gt;when clause contexts&lt;/a&gt; that still can be used in the &lt;code&gt;when&lt;/code&gt; clauses in &lt;code&gt;keybindings.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can get the list of when clause contexts including such undocumented ones by running the following steps.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Clone the &lt;a href="https://github.com/microsoft/vscode"&gt;VSCode repository&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Run the command below at the repository root.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rni&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"RawContextKey(&amp;lt;.*&amp;gt;)?&lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="s2"&gt;'.*&lt;/span&gt;&lt;span class="se"&gt;\)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; src/&lt;span class="k"&gt;*&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"s/^.*RawContextKey(&amp;lt;.*&amp;gt;)?&lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="s2"&gt;([^)]*)&lt;/span&gt;&lt;span class="se"&gt;\)&lt;/span&gt;&lt;span class="s2"&gt;.*/&lt;/span&gt;&lt;span class="se"&gt;\2&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"s/^.*RawContextKey(&amp;lt;.*&amp;gt;)?&lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="s2"&gt;([^)]*)&lt;/span&gt;&lt;span class="se"&gt;\)&lt;/span&gt;&lt;span class="s2"&gt;.*/&lt;/span&gt;&lt;span class="se"&gt;\2&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"s/'([^']*)'.*/&lt;/span&gt;&lt;span class="se"&gt;\1&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see the result at the bottom of this page.&lt;/p&gt;

&lt;p&gt;We can get only the context names with this command, so we have to infer what each one is from its name or by reading the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;It looks like the &lt;code&gt;RawContextKey&lt;/code&gt; class is used to register the contexts in the VSCode source code, so this command extracts the literal arguments passed to that class.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some exceptions
&lt;/h2&gt;

&lt;p&gt;Some arguments passed to &lt;code&gt;new RawContextKey()&lt;/code&gt; are not literals but variables that the command can't extract, so we have to read the code to get the context names in such cases.&lt;/p&gt;

&lt;p&gt;The following command returns all such lines at least.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rni&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"RawContextKey(&amp;lt;.*&amp;gt;)?&lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="s2"&gt;[^'].*&lt;/span&gt;&lt;span class="se"&gt;\)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; src/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;p&gt;These are not documented, so not official.&lt;/p&gt;

&lt;h2&gt;
  
  
  The result
&lt;/h2&gt;

&lt;p&gt;Here is the list obtained from &lt;a href="https://github.com/microsoft/vscode/tree/1.75.0"&gt;VSCode 1.75.0&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LinkedEditingInputVisible
acceptSuggestionOnEnter
accessibilityHelpWidgetVisible
accessibilityHelpWidgetVisible
accessibilityModeEnabled
activeAuxiliary
activeEditor
activeEditorAvailableEditorIds
activeEditorCanRevert
activeEditorCanSplitInGroup
activeEditorGroupEmpty
activeEditorGroupIndex
activeEditorGroupLast
activeEditorGroupLocked
activeEditorIsDirty
activeEditorIsFirstInGroup
activeEditorIsLastInGroup
activeEditorIsNotPreview
activeEditorIsPinned
activeEditorIsReadonly
activeLogOutput
activeLogOutput
activeOutputChannel
activePanel
activeViewlet
allWalkthroughsHidden
alwaysShowInlineSuggestionToolbar
atEndOfWord
autoSaveAfterShortDelayContext
auxiliaryBarFocus
auxiliaryBarVisible
bannerFocused
breadcrumbsActive
breadcrumbsPossible
breadcrumbsVisible
breakWhenValueChangesSupported
breakWhenValueIsAccessedSupported
breakWhenValueIsReadSupported
breakpointInputFocused
breakpointItemType
breakpointSupportsCondition
breakpointWidgetVisible
breakpointsExist
breakpointsFocused
builtInExtensions
callHierarchyDirection
callHierarchyVisible
callStackItemStopped
callStackItemType
callStackSessionHasOneThread
callStackSessionIsAttach
canNavigateBack
canNavigateBackInEditLocations
canNavigateBackInNavigationLocations
canNavigateForward
canNavigateForwardInEditLocations
canNavigateForwardInNavigationLocations
canNavigateToLastEditLocation
canNavigateToLastNavigationLocation
canRedo
canReopenClosedEditor
canToggleWordWrap
canUndo
canUndoInlineSuggestion
canViewMemory
cancellableOperation
codeActionMenuVisible
commentEditorFocused
commentIsEmpty
commentThreadIsEmpty
commentsFilterFocus
commentsView.hasComments
commentsView.showResolvedFilter
commentsView.showUnResolvedFilter
currentProfile
customExecutionSupported
customTreeView
debugConfigurationType
debugExtensionAvailable
debugProtocolVariableMenuContext
debugSetExpressionSupported
debugSetVariableSupported
debugState
debugType
debuggerInterestedInActiveEditor
debuggersAvailable
defaultExtensionViews
dirtyDiffVisible
dirtyEditorFocusedInOpenEditors
dirtyWorkingCopies
disassembleRequestSupported
disassemblyViewFocus
downloadUrl
editSessionsShowView
editorAreaVisible
editorColumnSelection
editorFocus
editorHasCallHierarchyProvider
editorHasCodeActionsProvider
editorHasCodeLensProvider
editorHasCompletionItemProvider
editorHasDeclarationProvider
editorHasDefinitionProvider
editorHasDocumentFormattingProvider
editorHasDocumentHighlightProvider
editorHasDocumentSelectionFormattingProvider
editorHasDocumentSymbolProvider
editorHasHoverProvider
editorHasImplementationProvider
editorHasInlayHintsProvider
editorHasMultipleDocumentFormattingProvider
editorHasMultipleDocumentSelectionFormattingProvider
editorHasMultipleSelections
editorHasReferenceProvider
editorHasRenameProvider
editorHasSelection
editorHasSignatureHelpProvider
editorHasTypeDefinitionProvider
editorHasTypeHierarchyProvider
editorHoverVisible
editorIsOpen
editorLangId
editorReadonly
editorSimpleInput
editorTabMovesFocus
editorTabsVisible
editorTextFocus
editorWordWrap
embedderIdentifier
emptyWorkspaceSupport
enterMultiRootWorkspaceSupport
exceptionWidgetVisible
explorerResourceAvailableEditorIds
explorerResourceCut
explorerResourceIsFolder
explorerResourceIsRoot
explorerResourceMoveableToTrash
explorerResourceReadonly
explorerViewletCompressedFirstFocus
explorerViewletCompressedFocus
explorerViewletCompressedLastFocus
explorerViewletFocus
explorerViewletVisible
expressionSelected
extensionHostProfileRecorded
extensionSearchHasText
extensionsSortByValue
fileCopied
fileMatchFocus
fileMatchOrFolderMatchFocus
fileMatchOrFolderMatchWithResourceFocus
fileMatchOrMatchFocus
filesExplorerFocus
findInputFocussed
findWidgetVisible
firstMatchFocus
focusedCustomEditorIsEditable
focusedSessionIsAttach
focusedStackFrameHasInstructionReference
focusedView
folderMatchFocus
folderMatchWithResourceFocus
foldingEnabled
forwardedPortsViewEnabled
groupEditorsCount
groupFocusedInOpenEditors
hasConflicts
hasDebugged
hasGallery
hasInstalledExtensions
hasLocalServer
hasMultipleNewFileEntries
hasNextTabstop
hasOtherSuggestions
hasOutdatedExtensions
hasPrevTabstop
hasProfiles
hasRemoteServer
hasSearchResult
hasSnippetCompletions
hasSymbols
hasWebFileSystemAccess
hasWebServer
hasWordHighlights
inBreakpointWidget
inCompositeEditor
inDebugMode
inDebugRepl
inDiffEditor
inKeybindings
inKeybindingsSearch
inOutput
inOutput
inReferenceSearchEditor
inSearchEditor
inSettingsEditor
inSettingsEditorUserTab
inSettingsJSONEditor
inSettingsSearch
inSnippetMode
inTreeView
inWelcome
inZenMode
inlineSuggestionHasIndentation
inlineSuggestionHasIndentationLessThanTabSize
inlineSuggestionVisible
inputBoxFocus
interactiveInputCursorAtBoundary
interactivePlaygroundFocus
interfaceOverviewVisible
isCenteredLayout
isCurrentProfileTransient
isDevelopment
isExtensionBisectActive
isFileSystemResource
isFullscreen
isIOS
isInEmbeddedEditor
isLinux
isMac
isMacNative
isMergeEditor
isMergeResultEditor
isMobile
isProfileExportInProgress
isProfileImportInProgress
isReadingLineWithInlayHints
isWeb
isWindows
isWorkspaceTrustEnabled
isWorkspaceTrusted
jumpToCursorSupported
keybindingFocus
languageSupportsDisassembleRequest
listDoubleSelection
listFocus
listHasSelectionOrFocus
listMultiSelection
listSelectionNavigation
listSupportsFind
listSupportsMultiselect
loadedScriptsItemType
loadedScriptsSupported
localHistoryItemSelectedForCompare
markersNavigationVisible
matchFocus
mergeEditorBaseUri
mergeEditorLayout
mergeEditorResultUri
mergeEditorShowBase
mergeEditorShowBaseAtTop
mergeEditorShowNonConflictingChanges
messageVisible
multiCursorModifier
multiSessionDebug
multiSessionRepl
multipleEditorGroups
notebookBreakpointMargin
notebookCellEditable
notebookCellEditorFocused
notebookCellExecuting
notebookCellExecutionState
notebookCellFocused
notebookCellHasOutputs
notebookCellInputIsCollapsed
notebookCellLineNumbers
notebookCellListFocused
notebookCellMarkdownEditMode
notebookCellOutputIsCollapsed
notebookCellResource
notebookCellToolbarLocation
notebookCellType
notebookCursorNavigationMode
notebookDiffCellInputChanged
notebookDiffCellPropertyChanged
notebookDiffCellPropertyExpanded
notebookEditable
notebookEditorCursorAtBoundary
notebookEditorFocused
notebookFindWidgetFocused
notebookHasOutputs
notebookHasRunningCell
notebookInterruptibleKernel
notebookKernel
notebookKernelCount
notebookKernelSelected
notebookKernelSourceCount
notebookLastCellFailed
notebookMissingKernelExtension
notebookOutputFocused
notebookType
notebookUseConsolidatedOutputButton
notificationCenterVisible
notificationFocus
notificationToastsVisible
openEditorsFocus
openEditorsVisible
openFolderWorkspaceSupport
openPreviewEnabled
outlineAllCollapsed
outlineFiltersOnType
outlineFollowsCursor
outlineSortMode
panelAlignment
panelFocus
panelMaximized
panelPosition
panelVisible
parameterHintsMultipleSignatures
parameterHintsVisible
patternExcludesInputBoxFocus
patternIncludesInputBoxFocus
portChangable
problemFocus
problems.filter.activeFile
problems.filter.errors
problems.filter.excludedFiles
problems.filter.info
problems.filter.warnings
problemsFilterFocus
problemsViewMode
problemsVisibility
processExecutionSupported
productQualityType
profileSessionState
profiles.enabled
readonlyEditorFocusedInOpenEditors
recommendedExtensions
refactorPreview.enabled
refactorPreview.groupByFile
refactorPreview.hasCategories
refactorPreview.hasCheckedChanges
referenceSearchVisible
relatedInformationFocus
releaseNotesUrl
remoteConnectionState
remoteFileDialogVisible
remoteName
renameInputVisible
replaceActive
replaceInputBoxFocus
replaceInputFocussed
resource
resourceDirname
resourceExtname
resourceFilename
resourceLangId
resourcePath
resourceScheme
resourceSelectedForCompare
resourceSet
restartFrameSupported
scmProvider
scmProviderHasRootUri
scmProviderRootUri
scmRepositoryCount
scmRepositorySortKey
scmRepositoryVisibleCount
scmViewModelAreAllRepositoriesCollapsed
scmViewModelIsAnyRepositoryCollapsible
scmViewModelMode
scmViewModelSortKey
searchBuiltInExtensions
searchDeprecatedExtensions
searchDisabledExtensions
searchEnabledExtensions
searchExtensionUpdates
searchInputBoxFocus
searchInstalledExtensions
searchMarketplaceExtensions
searchOutdatedExtensions
searchRecentlyUpdatedExtensions
searchState
searchUnsupportedWorkspaceExtensions
searchViewletFocus
searchViewletVisible
selectionAnchorSet
serverlessWebContext
settingRowFocus
settingsTocRowFocus
shellExecutionSupported
showPreReleaseVersion
sideBarFocus
sideBarVisible
sideBySideEditorActive
sortByUpdateDate
splitEditorsVertically
stackFrameSupportsRestart
statusBarFocused
stepBackSupported
stepIntoTargetsSupported
suggestWidgetDetailsVisible
suggestWidgetHasFocusedSuggestion
suggestWidgetMultipleSuggestions
suggestWidgetVisible
suggestWidgetVisible
suggestionCanResolve
suggestionHasInsertAndReplaceRange
suggestionInsertMode
suggestionMakesTextEdit
supportedCodeAction
suspendDebuggeeSupported
syncEnabled
syncStatus
taskCommandsRegistered
taskRunning
temporaryWorkspace
terminateDebuggeeSupported
testing.activeEditorHasTests
testing.autoRun
testing.canRefresh
testing.explorerViewMode
testing.explorerViewSorting
testing.hasAnyResults
testing.hasConfigurableProfile
testing.hasCoverableTests
testing.hasDebuggableTests
testing.hasNonDefaultProfile
testing.hasRunnableTests
testing.isContinuousModeOn
testing.isInPeek
testing.isPeekVisible
testing.isRefreshing
testing.isRunning
testing.providerCount
testing.supportsContinuousRun
textCompareEditorActive
textCompareEditorVisible
textInputFocus
timelineExcludeSources
timelineFollowActiveEditor
timelineHasProvider
treeElementCanCollapse
treeElementCanExpand
treeElementHasChild
treeElementHasParent
treeFindOpen
tunnelCloseable
tunnelPrivacy
tunnelPrivacyEnabled
tunnelProtocol
tunnelType
tunnelViewFocus
typeHierarchyDirection
typeHierarchyVisible
updateState
userDataSyncAccountStatus
userDataSyncTurningOn
userHasOpenedNotebook
variableEvaluateNamePresent
variableIsReadonly
variablesFocused
viewHasFilePattern
viewHasReplacePattern
viewHasSearchPattern
viewHasSomeCollapsibleItem
viewHasSomeCollapsibleResult
virtualWorkspace
watchExpressionsExist
watchExpressionsFocused
watchItemType
webviewFindWidgetEnabled
webviewFindWidgetFocused
webviewFindWidgetVisible
whenFocus
workbenchState
workspaceFolderCount
workspacePlatform

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>vscode</category>
      <category>extension</category>
    </item>
    <item>
      <title>Streamlit meets WebAssembly - stlite</title>
      <dc:creator>Yuichiro Tachibana (Tsuchiya)</dc:creator>
      <pubDate>Wed, 09 Nov 2022 07:58:09 +0000</pubDate>
      <link>https://dev.to/whitphx/streamlit-meets-webassembly-stlite-2n07</link>
      <guid>https://dev.to/whitphx/streamlit-meets-webassembly-stlite-2n07</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;I created a &lt;strong&gt;WebAssembly&lt;/strong&gt; (&lt;strong&gt;Wasm&lt;/strong&gt;) port of &lt;strong&gt;Streamlit&lt;/strong&gt;, "&lt;em&gt;stlite&lt;/em&gt;".&lt;/li&gt;
&lt;li&gt;You can try it out and share your apps on "&lt;a href="https://edit.share.stlite.net/" rel="noopener noreferrer"&gt;&lt;strong&gt;stlite sharing&lt;/strong&gt;&lt;/a&gt;", the online code editor + app sharing platform for &lt;em&gt;stlite&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;The Wasm-based runtime provides many benefits such as offline capability, data privacy, and scalability.&lt;/li&gt;
&lt;li&gt;In addition to &lt;a href="https://edit.share.stlite.net/" rel="noopener noreferrer"&gt;stlite sharing&lt;/a&gt;, you can also host your Streamlit/stlite apps &lt;a href="https://github.com/whitphx/stlite#use-stlite-on-your-web-page" rel="noopener noreferrer"&gt;on your web site&lt;/a&gt; or create &lt;a href="https://github.com/whitphx/stlite/tree/main/packages/desktop-cli#readme" rel="noopener noreferrer"&gt;offline desktop applications&lt;/a&gt;. So you can create multi-platform apps only with Python coding.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Streamlit: a great Python web app framework
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://streamlit.io/" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstreamlit.io%2Fimages%2Fbrand%2Fstreamlit-logo-secondary-colormark-darktext.svg" alt="Streamlit logo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://streamlit.io/" rel="noopener noreferrer"&gt;Streamlit&lt;/a&gt; is a web app framework with which we can create interactive web apps only with Python coding. It is especially famous and popular for data apps development as it well supports many data widgets cooperating the DS/ML Python ecosystem, while it also covers a wide range of general purpose web development.&lt;/p&gt;



&lt;p&gt;When you run a Streamlit app, the Streamlit framework starts a web server and it serves a frontend app that is a single page application (SPA) whose contents are dynamically constructed according to your app script written in Python. Then the frontend app continuously communicates with the web server and triggers the Python code executions on the server upon some events to get some results that dynamically update the frontend view.&lt;br&gt;
Due to this unique server-client architecture, we can construct interactive web apps only by writing the logics in the server-side Python code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpgx1gq0ald3hmeaoyxr8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpgx1gq0ald3hmeaoyxr8.png" alt="Streamlit architecture"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  stlite: client-side Streamlit powered by Pyodide
&lt;/h2&gt;

&lt;p&gt;There had been an epoch-making invent in the Python world - &lt;a href="https://pyodide.org/" rel="noopener noreferrer"&gt;Pyodide&lt;/a&gt;. It is a CPython runtime compiled for WebAssembly (Wasm) that runs on web browsers (and NodeJS).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pyodide.org/" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpyodide.org%2Fen%2Fstable%2F_static%2Fpyodide-logo.png" alt="Pyodide logo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So I customized Streamlit to run on Pyodide runtime and released it as "&lt;a href="https://github.com/whitphx/stlite" rel="noopener noreferrer"&gt;stlite&lt;/a&gt;"!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/whitphx/stlite" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fwhitphx%2Fstlite%2Fmain%2Fdocs%2Fimages%2Flogo.svg" alt="stlite logo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On &lt;em&gt;stlite&lt;/em&gt;, the entry point is a JavaScript file that mounts the Streamlit frontend SPA into the HTML page, loads the Pyodide environment, and launches the Streamlit Python server in the Pyodide environment &lt;strong&gt;on the web browser&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;With this architecture, thanks to Pyodide, the Python runtime no longer exists on the server side since it runs on the web browser.&lt;br&gt;
The web server is only for serving the static files such as HTML, JS, and CSS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fonp81kzidbvop626g3c1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fonp81kzidbvop626g3c1.png" alt="stlite architecture"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Pros and Cons
&lt;/h2&gt;

&lt;p&gt;As it runs completely on the browsers, &lt;em&gt;stlite&lt;/em&gt; has some benefits that the original Streamlit does not have.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Offline capability&lt;/strong&gt;:&lt;br&gt;
As even the Streamlit "server" runs on the browser, all the components resides on the client side, so once all the resources are loaded from the web server, the app can run offline.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data privacy&lt;/strong&gt;:&lt;br&gt;
Since the entire app runs on your browser, even when you do "upload" some files from the file uploader on the page, these files are NEVER sent to any remote servers and only processed within your machine.&lt;br&gt;
This feature is sometimes beneficial especially in the applications of data science, machine learning, or statistics where Streamlit is widely used, as these often have strict privacy rules.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scalability&lt;/strong&gt;:&lt;br&gt;
The main workload such as machine learning computation written in Python moves from the server to each browser, so the system becomes scalable.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Multi-platform (web, desktop, mobile)&lt;/strong&gt;:&lt;br&gt;
As it runs on web browsers, it can also be an installable app (&lt;a href="https://web.dev/progressive-web-apps/" rel="noopener noreferrer"&gt;PWA&lt;/a&gt;), and can be bundled into a desktop app with Electron. Building mobile apps is also on the read map.&lt;br&gt;
As a result, you can create interactive apps only in Python with Streamlit and bundle them for each platform.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Online editing experience&lt;/strong&gt;:&lt;br&gt;
I developed the online editor &amp;amp; real-time preview service for Streamlit apps based on &lt;em&gt;stlite&lt;/em&gt; - &lt;strong&gt;stlite sharing&lt;/strong&gt; that we will see below soon.&lt;br&gt;
Precisely, this is not purely because of WebAssembly, but the Wasm-based architecture made it easier to create such a service which could not have existed before.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the other hand, &lt;em&gt;stlite&lt;/em&gt; and Pyodide has some disadvantages as a tradeoff.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Some packages are not available&lt;/strong&gt;:&lt;br&gt;
Some C extension packages such as TensorFlow cannot be installed because C extensions must be compiled for the Pyodide runtime specifically, while many popular C extensions are already &lt;a href="https://github.com/pyodide/pyodide/tree/main/packages" rel="noopener noreferrer"&gt;available&lt;/a&gt; such as NumPy or Pandas.&lt;br&gt;
For more details, read the Pyodide articles such as &lt;a href="https://pyodide.org/en/stable/usage/faq.html#micropip-can-t-find-a-pure-python-wheel" rel="noopener noreferrer"&gt;this one&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Large initial payload&lt;/strong&gt;:&lt;br&gt;
A large amount of resources will be downloaded when the user opens the app because Pyodide loads the whole Python runtime and the standard libraries, and &lt;em&gt;stlite&lt;/em&gt; also downloads the necessary wheel files including the &lt;code&gt;streamlit&lt;/code&gt; package.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Network restriction&lt;/strong&gt;:&lt;br&gt;
For the security reasons, accessing remote resources from the &lt;em&gt;stlite&lt;/em&gt; applications are restricted by the browser, e.g. CORS.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The source code is open&lt;/strong&gt;:&lt;br&gt;
Note that all the source code and hosted data are sent to the client side, so they are visible to the users. You must not put any secrets on the source code of the &lt;em&gt;stlite&lt;/em&gt; apps.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(This section is highly inspired by &lt;a href="https://shiny.rstudio.com/py/docs/shinylive.html" rel="noopener noreferrer"&gt;the blog post about Shinylive&lt;/a&gt;.)&lt;/p&gt;
&lt;h2&gt;
  
  
  Online code editor + app sharing platform: stlite sharing
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://edit.share.stlite.net/" rel="noopener noreferrer"&gt;&lt;strong&gt;stlite sharing&lt;/strong&gt;&lt;/a&gt; is an online code editor + app sharing platform for &lt;em&gt;stlite&lt;/em&gt;. It is the easiest and fastest way to try out &lt;em&gt;stlite&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://edit.share.stlite.net/" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fedit.share.stlite.net%2Fogp.png" alt="stlite sharing OGP"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The following screenshot is the online editing &amp;amp; app preview mode of &lt;strong&gt;stlite sharing&lt;/strong&gt;. You can try it out at &lt;a href="https://edit.share.stlite.net/" rel="noopener noreferrer"&gt;https://edit.share.stlite.net/&lt;/a&gt; .&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftkxu3ygvmuczrfdna74z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftkxu3ygvmuczrfdna74z.png" alt="stlite sharing screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you create/edit/delete the files on the left pane, those changes are applied to the virtual file system on top of which &lt;em&gt;stlite&lt;/em&gt; runs, then the Streamlit server detects the file changes and recommends you to hot-reload the application, as shown in the screenshot below.&lt;br&gt;
(The file change detection and hot-reloading is a part of the Streamlit core features. If you are not familiar with it, read &lt;a href="https://docs.streamlit.io/library/get-started/main-concepts#development-flow" rel="noopener noreferrer"&gt;the official document&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj3cg7gtj63j8tj38xykg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj3cg7gtj63j8tj38xykg.png" alt="stlite sharing screenshot on the file change"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The file changes are saved to the file system when the "Save" button is clicked.&lt;br&gt;
You can also add or delete the files on the file tabs.&lt;/p&gt;
&lt;h3&gt;
  
  
  Shareable URLs
&lt;/h3&gt;

&lt;p&gt;On &lt;em&gt;stlite sharing&lt;/em&gt;, you can get the shareable URL of the current app displayed at top right. There is also the "Open App" link next to it, through which you can open the URL.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqflpbgizkkzmqe5xq4lu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqflpbgizkkzmqe5xq4lu.png" alt="stlite sharing screenshot on the file change"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By navigating to the URL or clicking the link, the app opens in the sharing mode as below, where only the Streamlit app opens and there are not the editor, the sharing link, and any other widgets.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6s0dil6o399kfkfxo4l9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6s0dil6o399kfkfxo4l9.png" alt="stlite sharing screenshot on the app sharing mode"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Its URL is &lt;code&gt;https://share.stlite.net/&lt;/code&gt; with a long hash like &lt;code&gt;#!ChBz...&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The host &lt;code&gt;https://share.stlite.net/&lt;/code&gt; is for the app sharing mode of &lt;em&gt;stlite sharing&lt;/em&gt; while the editor mode we have seen above is at &lt;code&gt;https://edit.share.stlite.net/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the long hash part of the URL like &lt;code&gt;#!ChBz...&lt;/code&gt;,&lt;br&gt;
all the source code and data edited on the editor are encoded and embedded, so you can share the app only by copying this URL.&lt;/p&gt;

&lt;p&gt;Another good point of this approach is, since all the code and data are embedded into the URL hash, these data are &lt;a href="https://en.wikipedia.org/wiki/URI_fragment#Basics" rel="noopener noreferrer"&gt;never uploaded and saved to the remote server&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The encoded URL hash is also loadable on the editor mode, so if you open the editor mode URL, https://&lt;strong&gt;edit&lt;/strong&gt;.share.stlite.net/ with the hash, the app data will be restored on the editor.&lt;/p&gt;

&lt;p&gt;Note that some SNS and URL shortening services cut off the long URLs, so you should take care of it when sharing the URL on such platforms. For the details, read &lt;a href="https://shiny.rstudio.com/py/docs/shinylive.html#sharing-shinylive-applications" rel="noopener noreferrer"&gt;this part of the Shinylive article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This URL mechanism is inspired by &lt;a href="https://shiny.rstudio.com/py/docs/shinylive.html" rel="noopener noreferrer"&gt;Shinylive&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Adding files
&lt;/h3&gt;

&lt;p&gt;By clicking the "+" button at the file tabs area, you can create a new file.&lt;/p&gt;

&lt;p&gt;You can also upload files from your local env (the files are never "uploaded" to any remote servers. They are just sent to the virtual file system on your browser.) Though this uploader, &lt;strong&gt;you can also add binary files&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyvey2sfdasu7otftx9zq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyvey2sfdasu7otftx9zq.png" alt="Add and upload buttons"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Files in directories and Multipage apps support
&lt;/h3&gt;

&lt;p&gt;You can edit the file name on the tab, and importantly, by inserting the file path delimiter, &lt;code&gt;/&lt;/code&gt;, you can create files in directories.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx14wq2xew0fw55fdcppe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx14wq2xew0fw55fdcppe.png" alt="Editing the file name on the tab"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this feature, &lt;em&gt;stlite sharing&lt;/em&gt; supports the Streamlit's &lt;strong&gt;Multipage apps&lt;/strong&gt;.&lt;br&gt;
In short, by creating a Python file in the &lt;code&gt;pages/&lt;/code&gt; directory (&lt;code&gt;pages/*.py&lt;/code&gt;), you can add a new page in the app.&lt;br&gt;
For the details, read the &lt;a href="https://docs.streamlit.io/library/get-started/multipage-apps" rel="noopener noreferrer"&gt;official document about Multipage apps&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Installing packages
&lt;/h3&gt;

&lt;p&gt;When you need to install some packages, use the special "requirements" tab.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft9jrug4d4q75bco9dcde.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft9jrug4d4q75bco9dcde.png" alt="The requirements tab"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The package names written in the editor on this tab will be passed to the package installer &lt;a href="https://pyodide.org/en/stable/usage/api/micropip-api.html#micropip.install" rel="noopener noreferrer"&gt;&lt;code&gt;micropip.install()&lt;/code&gt;&lt;/a&gt; internally (on the Pyodide runtime, &lt;a href="https://pyodide.org/en/stable/usage/api/micropip-api.html" rel="noopener noreferrer"&gt;&lt;code&gt;micropip&lt;/code&gt;&lt;/a&gt; is used as a package manager) when clicking the "Save" button.&lt;br&gt;
Write one package name per line.&lt;/p&gt;

&lt;p&gt;This is a special tab in that the file content is not saved into the file system. It is used only for specifying the package names passed to &lt;code&gt;micropip.install()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Please note again that some C extension packages cannot be installed. The following screenshot is the sample of such a case, where installing a C extension failed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv17asuqzkhcx26evloaz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv17asuqzkhcx26evloaz.png" alt="Installation failure"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Samples
&lt;/h3&gt;

&lt;p&gt;You can find some samples on the side menu, so check out these! I hope some inspire you.&lt;/p&gt;

&lt;p&gt;For example, the "Streamlit Hello" demo runs the official Streamlit demo on &lt;em&gt;stlite&lt;/em&gt; while it is available through the &lt;code&gt;streamlit hello&lt;/code&gt; command with the original Streamlit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fht32938t1ydf0u3ubljo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fht32938t1ydf0u3ubljo.png" alt="streamlit hello demo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With a recent release of Pyodide &lt;a href="https://blog.pyodide.org/posts/0.21-release/" rel="noopener noreferrer"&gt;0.21&lt;/a&gt;, many C-extensions became available including OpenCV.&lt;br&gt;
&lt;em&gt;stlite sharing&lt;/em&gt; includes the samples using OpenCV and scikit-image with real-time video streaming. Even these samples run on web browsers and nothing are sent to any remote servers!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpqqy4ybzib418ydysmjx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpqqy4ybzib418ydysmjx.png" alt="OpenCV demo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1583352801195065346-643" src="https://platform.twitter.com/embed/Tweet.html?id=1583352801195065346"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1583352801195065346-643');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1583352801195065346&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h3&gt;
  
  
  Loading apps from GitHub or Gist
&lt;/h3&gt;

&lt;p&gt;Instead of the encoded URL hash explained above,&lt;br&gt;
you can host your Python script somewhere and pass its URL to &lt;em&gt;stlite sharing&lt;/em&gt; via the URL hash as below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://share.stlite.net/#https://...py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the URL is of GitHub or Gist, you can pass the preview page URL instead of the raw file URL.&lt;br&gt;
For example, &lt;a href="https://share.stlite.net/#https://github.com/napoles-uach/test/blob/main/app.py" rel="noopener noreferrer"&gt;https://share.stlite.net/#https://github.com/napoles-uach/test/blob/main/app.py&lt;/a&gt; works.&lt;/p&gt;

&lt;p&gt;When you need to install some packages, use the following format instead.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://share.stlite.net/#url=&amp;lt;Script URL&amp;gt;?req=&amp;lt;Package1&amp;gt;&amp;amp;req=&amp;lt;Package2&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The example of this format installing &lt;code&gt;opencv-python&lt;/code&gt; and &lt;code&gt;matplotlib&lt;/code&gt; follows: &lt;a href="https://share.stlite.net/#url=https://github.com/whitphx/stlite-sample/blob/main/opencv-image-processing.py&amp;amp;req=opencv-python&amp;amp;req=matplotlib" rel="noopener noreferrer"&gt;https://share.stlite.net/#url=https://github.com/whitphx/stlite-sample/blob/main/opencv-image-processing.py&amp;amp;req=opencv-python&amp;amp;req=matplotlib&lt;/a&gt;&lt;br&gt;
There is also the Gist version: &lt;a href="https://share.stlite.net/#url=https://gist.github.com/whitphx/f23b7b2bbda19cd421121bd72ebf2101&amp;amp;req=opencv-python&amp;amp;req=matplotlib" rel="noopener noreferrer"&gt;https://share.stlite.net/#url=https://gist.github.com/whitphx/f23b7b2bbda19cd421121bd72ebf2101&amp;amp;req=opencv-python&amp;amp;req=matplotlib&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Host your Streamlit app on your web site
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;stlite&lt;/em&gt; also supports self-hosting the apps.&lt;/p&gt;

&lt;p&gt;All you have to do is write and host a single HTML file.&lt;/p&gt;

&lt;p&gt;It would be something like below, where you can embed the Python source code into the JS code as a string literal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;http-equiv=&lt;/span&gt;&lt;span class="s"&gt;"X-UA-Compatible"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"IE=edge"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt;
      &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt;
      &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1, shrink-to-fit=no"&lt;/span&gt;
    &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;stlite app&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt;
      &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt;
      &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/@stlite/mountable@0.15.0/build/stlite.css"&lt;/span&gt;
    &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"root"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/@stlite/mountable@0.15.0/build/stlite.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
      &lt;span class="nx"&gt;stlite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`
import streamlit as st

name = st.text_input('Your name')
st.write("Hello,", name or "world")
`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;root&lt;/span&gt;&lt;span class="dl"&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;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you need to install some packages, specify the entrypoint file name, and/or use multiple files, use the following more flexible API.&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="nx"&gt;stlite&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;requirements&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;matplotlib&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// Packages to install&lt;/span&gt;
    &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;streamlit_app.py&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// The target file of the `streamlit run` command&lt;/span&gt;
    &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;streamlit_app.py&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
import streamlit as st
import matplotlib.pyplot as plt
import numpy as np

size = st.slider("Sample size", 100, 1000)

arr = np.random.normal(1, 1, size=size)
fig, ax = plt.subplots()
ax.hist(arr, bins=20)

st.pyplot(fig)
`&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After writing the HTML file, host it wherever you like, such as GitHub Pages.&lt;br&gt;
In your local env, you can test it with &lt;code&gt;python -m http.server 8000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This YouTube video may guide you to set it up on GitHub Pages (although the &lt;em&gt;stlite&lt;/em&gt; API used in the video is out-dated now.)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/VQdktxgbmmg" rel="noopener noreferrer"&gt;https://youtu.be/VQdktxgbmmg&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please read the &lt;a href="https://github.com/whitphx/stlite#use-stlite-on-your-web-page" rel="noopener noreferrer"&gt;README&lt;/a&gt; for the details about self-hosting apps!&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Bundle your Streamlit app as a desktop app
&lt;/h2&gt;

&lt;p&gt;The &lt;em&gt;stlite&lt;/em&gt; apps run on web browsers, so it is also possible to bundle them into desktop apps with Electron.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4zdoi9ekuth8ofwr8swh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4zdoi9ekuth8ofwr8swh.png" alt="desktop app sample"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1577750208364134421-374" src="https://platform.twitter.com/embed/Tweet.html?id=1577750208364134421"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1577750208364134421-374');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1577750208364134421&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;To create your own desktop app with &lt;em&gt;stlite&lt;/em&gt;,&lt;br&gt;
&lt;strong&gt;follow this instruction! -&amp;gt;&lt;/strong&gt; &lt;a href="https://github.com/whitphx/stlite/tree/main/packages/desktop-cli#readme" rel="noopener noreferrer"&gt;https://github.com/whitphx/stlite/tree/main/packages/desktop-cli#readme&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is a sample app repository and its distributable files too.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/whitphx/stlite-desktop-example" rel="noopener noreferrer"&gt;A sample app repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/whitphx/stlite-desktop-example/releases/tag/v0.2.0" rel="noopener noreferrer"&gt;Distributable files&lt;/a&gt; (the macOS app file is not signed, so the security alert may be shown)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tell us your story!
&lt;/h2&gt;

&lt;p&gt;When you create some apps with &lt;em&gt;stlite&lt;/em&gt;, please share it!&lt;/p&gt;

&lt;p&gt;If it's on &lt;strong&gt;stlite sharing&lt;/strong&gt;, all you have to do is copy and paste the URL 👍&lt;/p&gt;

&lt;p&gt;These are good places to share your apps, samples, or case studies!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/whitphx/stlite/discussions/categories/show-and-tell" rel="noopener noreferrer"&gt;&lt;strong&gt;stlite&lt;/strong&gt; GitHub Discussions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://discuss.streamlit.io/" rel="noopener noreferrer"&gt;Streamlit community forum&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>streamlit</category>
      <category>webassembly</category>
      <category>datascience</category>
    </item>
    <item>
      <title>My talk at EuroPython 2022</title>
      <dc:creator>Yuichiro Tachibana (Tsuchiya)</dc:creator>
      <pubDate>Sun, 17 Jul 2022 22:31:31 +0000</pubDate>
      <link>https://dev.to/whitphx/my-talk-at-europython-2022-2dlk</link>
      <guid>https://dev.to/whitphx/my-talk-at-europython-2022-2dlk</guid>
      <description>&lt;p&gt;I presented a talk at EuroPython 2022 in Dublin.&lt;/p&gt;

&lt;p&gt;The talk title was &lt;strong&gt;“real-time browser-ready computer vision apps with Streamlit.”&lt;/strong&gt; It was on 14th July, and its description is linked below.&lt;a href="https://ep2022.europython.eu/session/real-time-browser-ready-computer-vision-apps-with-streamlit"&gt;https://ep2022.europython.eu/session/real-time-browser-ready-computer-vision-apps-with-streamlit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Its contents were mainly based on the Medium post I wrote with a similar title, &lt;a href="https://towardsdatascience.com/developing-web-based-real-time-video-audio-processing-apps-quickly-with-streamlit-7c7bcd0bc5a8"&gt;“Developing Web-Based Real-Time Video/Audio Processing Apps Quickly with Streamlit”&lt;/a&gt;, but the talk contained live-demo, live-coding, and more storytelling introduction.&lt;/p&gt;

&lt;p&gt;The live-stream archive that contains my talk has already published as linked below (this link is pointing to my talk time), while the edited version will be open later:&lt;/p&gt;



&lt;p&gt;You can find my voice was so high-pitch and my hands moved randomly because I was so nervous, especially at the beginning… ironically, I could speck in such a high speed due to many practices I did 😂&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ea479eOX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://whitphx.info/static/b59a88cb81484af4b0a9d5eb55e618d9/828fb/me_talking.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ea479eOX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://whitphx.info/static/b59a88cb81484af4b0a9d5eb55e618d9/828fb/me_talking.jpg" alt="My picture at the talk" title="My picture at the talk" width="630" height="420"&gt;&lt;/a&gt;(&lt;a href="https://twitter.com/takanory"&gt;@takanory&lt;/a&gt; took this picture of me. Thanks.)&lt;/p&gt;

&lt;p&gt;Regardless of that, I could successfully present all the contents as I planned and some people said it was informative nice talk which made me so glad.&lt;/p&gt;

&lt;p&gt;It was nice that with this talk, I could (a bit) promote my OSS project that I’m dedicated to and have positive feedback.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>A library for editable live samples of React components, React Runner</title>
      <dc:creator>Yuichiro Tachibana (Tsuchiya)</dc:creator>
      <pubDate>Thu, 26 May 2022 07:37:40 +0000</pubDate>
      <link>https://dev.to/whitphx/a-library-for-editable-live-samples-of-react-components-react-runner-105o</link>
      <guid>https://dev.to/whitphx/a-library-for-editable-live-samples-of-react-components-react-runner-105o</guid>
      <description>&lt;p&gt;I recently &lt;a href="https://dev.to/whitphx/a-react-library-for-y-m-d-dropdown-date-picker-ecp"&gt;created a React component library&lt;/a&gt; and wanted to publish a demo page for it that shows what the library can do with sample code.&lt;/p&gt;

&lt;p&gt;The actual demo page is like the following. There are &lt;strong&gt;a code editor with preset sample code and a rendering result of the React component side by side&lt;/strong&gt;. Both are embedded in the demo page along with other elements.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://whitphx.github.io/react-ymd-date-select/" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvzbm9e29z42dxwazvxd7.png" alt="demo screenshot"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://whitphx.github.io/react-ymd-date-select/" rel="noopener noreferrer"&gt;The demo page of &lt;code&gt;react-ymd-date-select&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;To do that, &lt;strong&gt;&lt;a href="https://github.com/nihgwu/react-runner" rel="noopener noreferrer"&gt;React Runner&lt;/a&gt; was the best in my situation&lt;/strong&gt;, compared to the following alternatives.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/FormidableLabs/react-live" rel="noopener noreferrer"&gt;React Live&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;This does not support &lt;code&gt;import&lt;/code&gt; statements in the sample code.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://sandpack.codesandbox.io/" rel="noopener noreferrer"&gt;Sandpack&lt;/a&gt; (&lt;a href="https://www.npmjs.com/package/@codesandbox/sandpack-react" rel="noopener noreferrer"&gt;&lt;code&gt;@codesandbox/sandpack-react&lt;/code&gt;&lt;/a&gt;)

&lt;ul&gt;
&lt;li&gt;This does not support nested paths such as &lt;code&gt;import Something from "package/subpath"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/codesandbox/codesandbox-client/issues/6499" rel="noopener noreferrer"&gt;https://github.com/codesandbox/codesandbox-client/issues/6499&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Embedding sample code at build time with bundler’s setting as such as raw-loader for Webpack.

&lt;ul&gt;
&lt;li&gt;This requires 2 imports per example as below, but I did not like that due to its redundancy.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;SampleComponent&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./components/Sample&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;SampleComponentCode&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./components/Sample?raw&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

  &lt;span class="p"&gt;...&lt;/span&gt;

  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;LivePreview&lt;/span&gt;
    &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;SampleComponent&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;SampleComponentCode&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Requirements/conditions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;I wanted to show the sample code and the rendered React component side by side.

&lt;ul&gt;
&lt;li&gt;It’s better if users can edit the code and the result changes dynamically.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Those samples should also be used as E2E test suites, so each sample code should be self-contained and executable without implicit dependencies.

&lt;ul&gt;
&lt;li&gt;Each component file should import necessary packages and export a component.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The demo site is built with React.

&lt;ul&gt;
&lt;li&gt;The actual code base is &lt;a href="https://github.com/whitphx/react-ymd-date-select/tree/main/website" rel="noopener noreferrer"&gt;https://github.com/whitphx/react-ymd-date-select/tree/main/website&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The target library is for React, so sample code is also written with React.&lt;/li&gt;
&lt;li&gt;They are written in TypeScript.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Comparison
&lt;/h2&gt;
&lt;h3&gt;
  
  
  React Runner
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/nihgwu/react-runner" rel="noopener noreferrer"&gt;React Runner&lt;/a&gt; satisfied all my needs.&lt;/p&gt;

&lt;p&gt;One drawback of this is that it requires all the libraries that might be imported in the samples to be bundled into the web page. While it's natural and technically inevitable, it leads to bigger bundle size.&lt;br&gt;
Sandpack below may not have this problem.&lt;/p&gt;
&lt;h3&gt;
  
  
  React Live
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/FormidableLabs/react-live" rel="noopener noreferrer"&gt;React Live&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A popular library for this purpose. For example, &lt;a href="https://chakra-ui.com/" rel="noopener noreferrer"&gt;Chakra UI’s doc&lt;/a&gt; is using this for its editable examples like the screenshot below.&lt;br&gt;
&lt;a href="https://chakra-ui.com/docs/components/layout/box" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7gfiutzc31y0a8svskqr.png" alt="ChakraUI's editable example"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The author of &lt;a href="https://github.com/nihgwu/react-runner" rel="noopener noreferrer"&gt;React Runner&lt;/a&gt; &lt;a href="https://github.com/nihgwu/react-runner#react-live-runner" rel="noopener noreferrer"&gt;wrote&lt;/a&gt; that it was inspired by React Live. From its design, React Runner looks like a superset of React Live.&lt;/p&gt;

&lt;p&gt;I did not use React Live because I wanted to show complete and self-contained sample code including &lt;code&gt;import&lt;/code&gt; statements, but React Live did not support it.&lt;/p&gt;
&lt;h3&gt;
  
  
  Sandpack
&lt;/h3&gt;

&lt;p&gt;A component library of the famous online code editor, &lt;a href="https://codesandbox.io/" rel="noopener noreferrer"&gt;CodeSandbox&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As my project was using React, its variant for React, &lt;a href="https://www.npmjs.com/package/@codesandbox/sandpack-react" rel="noopener noreferrer"&gt;&lt;code&gt;@codesandbox/sandpack-react&lt;/code&gt;&lt;/a&gt; was the candidate.&lt;/p&gt;

&lt;p&gt;This was also a nice library, but I could not choose it because it does not support imports from nested paths such as &lt;code&gt;import Something from "package/subpath"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;My library was built both for ESM and CommonJS and the CommonJS version allowed such nested imports, so I wanted to show that.&lt;/p&gt;

&lt;p&gt;If this limitation is not a problem for you, Sandpack may be the best.&lt;/p&gt;
&lt;h3&gt;
  
  
  Embedding sample code at build time
&lt;/h3&gt;

&lt;p&gt;Some bundlers allow to import the file contents as string instead of normal modules. There are &lt;a href="https://v4.webpack.js.org/loaders/raw-loader/" rel="noopener noreferrer"&gt;&lt;code&gt;raw-loader&lt;/code&gt;&lt;/a&gt; for Webpack and &lt;a href="https://vitejs.dev/guide/features.html#static-assets" rel="noopener noreferrer"&gt;&lt;code&gt;?raw&lt;/code&gt; query&lt;/a&gt; for Vite.&lt;/p&gt;

&lt;p&gt;This was also a nice solution as I could have each sample code as a separate &lt;code&gt;*.jsx&lt;/code&gt; or &lt;code&gt;*.tsx&lt;/code&gt; file and that was comfortable to write the code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Sample.tsx&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;SampleComponent&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;This is a sample&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&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="nx"&gt;SampleComponent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// DemoSite.tsx&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;SampleComponent&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./components/Sample&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;SampleComponentCode&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./components/Sample?raw&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="p"&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="kd"&gt;function&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="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;LivePreview&lt;/span&gt;
      &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;SampleComponent&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;SampleComponentCode&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, it requires 2 imports per 1 sample code and it was redundant. Also, it does not provide live-editing experience.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>A React library for Y-M-D dropdown date picker</title>
      <dc:creator>Yuichiro Tachibana (Tsuchiya)</dc:creator>
      <pubDate>Wed, 11 May 2022 11:52:33 +0000</pubDate>
      <link>https://dev.to/whitphx/a-react-library-for-y-m-d-dropdown-date-picker-ecp</link>
      <guid>https://dev.to/whitphx/a-react-library-for-y-m-d-dropdown-date-picker-ecp</guid>
      <description>&lt;p&gt;I created &lt;a href="https://whitphx.github.io/react-ymd-date-select/" rel="noopener noreferrer"&gt;&lt;code&gt;react-ymd-date-select&lt;/code&gt;&lt;/a&gt;, a React library for Year, Month, and Day dropdown UI.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://whitphx.github.io/react-ymd-date-select/" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fwhitphx%2Freact-ymd-date-select%2Fmain%2Fdocs%2Fimg%2Fsamplepic.png" alt="react-ymd-date-select sample screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/whitphx/react-ymd-date-select" rel="noopener noreferrer"&gt;whitphx/react-ymd-date-select - GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We sometimes need Y-M-D dropdown UI like the screenshot above rather than calendar UI. For example, when asking users to select their birth dates, Y-M-D dropdown is preferable because calendar widgets are usually difficult to seek to far old years, and conversely, its benefits such as intuitively showing days-of-week are not needed.&lt;/p&gt;

&lt;p&gt;I actually encountered such a situation, however, I could not find any existing packages that meet my needs. In turn, building it by myself looks simple, but actually, there are some nuts as below.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generating the arrays of year, month, and day numbers and labels.&lt;/li&gt;
&lt;li&gt;Validating the combination of Y-M-D. For example, &lt;code&gt;2022-02-29&lt;/code&gt; (Feb 29, 2022) is an invalid combination - that date does not exist.&lt;/li&gt;
&lt;li&gt;Combining the 3 values from Y, M, D &lt;code&gt;&amp;lt;select &amp;gt;&lt;/code&gt;s into one date string, and integrating it with the form component state or form library, e.g. &lt;a href="https://react-hook-form.com/" rel="noopener noreferrer"&gt;React Hook Form&lt;/a&gt; through &lt;code&gt;value&lt;/code&gt; and &lt;code&gt;onChange&lt;/code&gt; props.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And sometimes there come more requirements like&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To set the default year.&lt;/li&gt;
&lt;li&gt;To show only year and month selects (hide the day dropdown).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, to handle these, I made a library &lt;a href="https://whitphx.github.io/react-ymd-date-select/" rel="noopener noreferrer"&gt;&lt;code&gt;react-ymd-date-select&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With the &lt;code&gt;useDateSelect()&lt;/code&gt; hook provided by this library, you can create original Y-M-D components like the sample below, focusing on the view behavior as cumbersome logic has been encapsulated into the hook.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;value&lt;/code&gt; prop and the argument of &lt;code&gt;onChange()&lt;/code&gt; are string formatted in &lt;code&gt;yyyy-MM-DD&lt;/code&gt;, which is ISO8601 format, and the same as the value of the native &lt;code&gt;&amp;lt;input type="date" /&amp;gt;&lt;/code&gt;. See &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/date#value" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/date#value&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See &lt;a href="https://whitphx.github.io/react-ymd-date-select/" rel="noopener noreferrer"&gt;the demo page&lt;/a&gt; for more information.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&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="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&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;useDateSelect&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="s2"&gt;react-ymd-date-select&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;CustomDateSelectProps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;CustomDateSelect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CustomDateSelectProps&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;dateSelect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useDateSelect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onChange&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="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"date"&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;dateSelect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dateValue&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;dateSelect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onDateChange&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Year
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;select&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;dateSelect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;yearValue&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;dateSelect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onYearChange&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;dateSelect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;yearOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;yearOption&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;yearOption&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;yearOption&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;yearOption&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Month
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;select&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;dateSelect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;monthValue&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;dateSelect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onMonthChange&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;dateSelect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;monthOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;monthOption&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;monthOption&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;monthOption&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;monthOption&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Day
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;select&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;dateSelect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dayValue&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;dateSelect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onDayChange&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;dateSelect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dayOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;dayOption&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;dayOption&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;dayOption&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;dayOption&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;option&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;select&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>How to ask about programming problems</title>
      <dc:creator>Yuichiro Tachibana (Tsuchiya)</dc:creator>
      <pubDate>Tue, 15 Mar 2022 11:20:08 +0000</pubDate>
      <link>https://dev.to/whitphx/how-to-ask-about-programming-problems-21f0</link>
      <guid>https://dev.to/whitphx/how-to-ask-about-programming-problems-21f0</guid>
      <description>&lt;p&gt;&lt;em&gt;Updated:&lt;/em&gt; &lt;a href="https://stackoverflow.com/help/how-to-ask"&gt;How do I ask a good question? - Help Center - Stack Overflow&lt;/a&gt; has already explained almost all things this article says.&lt;/p&gt;

&lt;h1&gt;
  
  
  Objective
&lt;/h1&gt;

&lt;p&gt;This article explains some things you should know when asking programming problems such as “X is not working” or “What’s this error?“.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This article mainly explains superficial and formal techniques that make the questions readable and likely to get responses.&lt;/li&gt;
&lt;li&gt;This article is NOT focusing on soft skills or moral advice like “be polite.”&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  The most important idea
&lt;/h1&gt;

&lt;p&gt;The principle is to pay as much effort as possible to save the respondents’ time and effort to understand and solve the problem.&lt;/p&gt;

&lt;p&gt;The problem-solving process they may do includes, for example,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reproducing the bug in their own environment by running the actual code&lt;/li&gt;
&lt;li&gt;reading the logs&lt;/li&gt;
&lt;li&gt;googling the error messages&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Things to do and not to do
&lt;/h1&gt;

&lt;p&gt;So, you should be aware of the following things to support solving the problem.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do research and try possible solutions by yourself beforehand as much as possible, and post the progress and results.

&lt;ul&gt;
&lt;li&gt;Information such as “I tried X but it didn’t work” or “I think Y may be the related topic, but couldn’t find a clear answer” is a good starting point for problem solving.&lt;/li&gt;
&lt;li&gt;Incidentally, such your effort motivates respondents. People want to support those who pay efforts.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Do provide all the information for the respondents to reproduce the problem.

&lt;ul&gt;
&lt;li&gt;It includes, but is not limited to, source code, assets, and/or environment information.&lt;/li&gt;
&lt;li&gt;Think whether it’s possible to run the code and encounter the same problem from zero only with the information you post?&lt;/li&gt;
&lt;li&gt;If the amount is huge, consider to&lt;/li&gt;
&lt;li&gt;upload them to GitHub and post the link.&lt;/li&gt;
&lt;li&gt;create a shorter code snippet that reproduces the same problem and post it.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Do share the background or the high-level requirements.

&lt;ul&gt;
&lt;li&gt;Why do you want to solve this problem? What do you want build after solving the problem? What kind of software/product are you developing?&lt;/li&gt;
&lt;li&gt;Such information is sometimes much important rather than the specific error description itself. Do not omit it just by your decision.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Do format the source code and the logs.

&lt;ul&gt;
&lt;li&gt;For example, if Markdown can be used in the forum, put

````` (three backticks) surrounding the code or logs&lt;sup id="fnref-1"&gt;1&lt;/sup&gt;.&lt;/li&gt;
&lt;li&gt;Markdown can be used in many web forums. Learn Markdown syntax. You have to use at least code blocks. See &lt;a href="https://www.markdownguide.org/extended-syntax/#fenced-code-blocks"&gt;https://www.markdownguide.org/extended-syntax/#fenced-code-blocks&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Non-formatted code and logs are very hard to read.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Don’t post the source code or the logs as screenshots.

&lt;ul&gt;
&lt;li&gt;They cannot be copied to the clipboard. It takes much effort to run the code or google the logs. It’s so annoying.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Do paste the images or the screenshots if you have visual results related to the problem.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I will be adding the list items. If you have ideas to add to the list, please let me know &lt;a href="https://twitter.com/whitphx"&gt;through Twitter&lt;/a&gt; or by &lt;a href="https://github.com/whitphx/whitphx.info/issues"&gt;creating GitHub Issues&lt;/a&gt; or &lt;a href="https://github.com/whitphx/whitphx.info/pulls"&gt;Pull Requests&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  More resources
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.propublica.org/nerds/how-to-ask-programming-questions"&gt;How to Ask Programming Questions — ProPublica&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codingkilledthecat.wordpress.com/2012/06/26/how-to-ask-for-programming-help/"&gt;How to Ask for Programming Help | Coding Killed the Cat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codereview.stackexchange.com/help/how-to-ask"&gt;How do I ask a good question? - Help Center - Code Review Stack Exchange&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://content.breatheco.de/how-to/ask"&gt;How to ask programming questions in just 5 steps - BreatheCode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://betterprogramming.pub/how-to-ask-questions-about-programming-dcd948fcd2bd"&gt;How to Ask Questions About Programming | by Martin Andersson Aaberge | Better Programming&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;ol&gt;
&lt;li&gt;Precisely, the code block with `&lt;code&gt;

&lt;/code&gt; is an extended Markdown syntax, such as &lt;a href="https://github.github.com/gfm/"&gt;GitHub-flavored Markdown (GFM)&lt;/a&gt;. Anyway, many web forums are supporting it.↩
&lt;/li&gt;
&lt;/ol&gt;

</description>
    </item>
    <item>
      <title>How to install Python for Intel chip on M1 Mac</title>
      <dc:creator>Yuichiro Tachibana (Tsuchiya)</dc:creator>
      <pubDate>Sun, 06 Feb 2022 08:37:59 +0000</pubDate>
      <link>https://dev.to/whitphx/how-to-install-python-for-intel-chip-on-m1-mac-104p</link>
      <guid>https://dev.to/whitphx/how-to-install-python-for-intel-chip-on-m1-mac-104p</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;To install Python 3.8.7 on M1 mac, for example,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;arch -arch x86_64 env PATH=${PATH/\/opt\/homebrew\/bin:/} pyenv install 3.8.7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is from &lt;a href="https://qiita.com/tomtsutom0122/items/52487730001247fdc2c5"&gt;M1版とIntel版のHomebrewを併用するときpyenvがうまく動かない問題を解決する - Qiita&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you encounter platform compatibility problems during package installation, upgrading pip sometimes resolves it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ pip install -U pip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the solution above is valid on a shell running without Rosetta. You can check the architecture with &lt;code&gt;arch&lt;/code&gt; command as below. It returns &lt;code&gt;arm64&lt;/code&gt; when running without Rosetta.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ arch
arm64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Install &lt;code&gt;homebrew&lt;/code&gt; both for Arm and x86_64.&lt;/li&gt;
&lt;li&gt;Set up &lt;code&gt;pyenv&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When is this necessary?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  When you have to use Python versions that do not have Arm build
&lt;/h3&gt;

&lt;p&gt;For example, Python &amp;lt;=3.8.&lt;/p&gt;

&lt;h3&gt;
  
  
  When you have to use packages that do not have Arm build
&lt;/h3&gt;

&lt;p&gt;Some Python packages include binaries compiled for each specific platform. If you use such packages and they do not have Arm-compatible binaries, you have to use Intel-compatible Python runtime, even if the Python runtime itself has Arm-compatible version.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example case
&lt;/h2&gt;

&lt;p&gt;I tried to use &lt;a href="https://github.com/whitphx/streamlit-webrtc/"&gt;&lt;code&gt;streamlit-webrtc&lt;/code&gt;&lt;/a&gt; with Python 3.9 on M1 Mac.&lt;/p&gt;

&lt;p&gt;NOTE: For app development using &lt;code&gt;streamlit-webrtc&lt;/code&gt;, see &lt;a href="//../20211231-streamlit-webrtc-video-app-tutorial/"&gt;this post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At first, I installed Python 3.9.3 for Arm via &lt;code&gt;pyenv&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ arch # arm64
$ pyenv install 3.9.3
$ pyenv shell 3.9.3
$ python -V # Python 3.9.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, inside a project directory, I set up a virtual env and installed necessary packages, &lt;code&gt;streamlit&lt;/code&gt; and &lt;code&gt;streamlit-webrtc&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ python -m venv .venv
$ . .venv/bin/activate
$ pip install -U pip
$ pip install streamlit
$ pip install streamlit-webrtc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, I created an example &lt;code&gt;app.py&lt;/code&gt; file as below (This is a Streamlit app script. See &lt;a href="//../20211231-streamlit-webrtc-video-app-tutorial/"&gt;this post&lt;/a&gt; for the details),&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from streamlit_webrtc import webrtc_streamer

webrtc_streamer(key="sample")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and run the Streamlit app with the command below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ streamlit run app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the following error occurred.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Traceback (most recent call last):
  File "/path/to/python39-m1test/.venv/bin/streamlit", line 5, in &amp;lt;module&amp;gt;
    from streamlit.cli import main
  File "/path/to/python39-m1test/.venv/lib/python3.9/site-packages/streamlit/ __init__.py", line 72, in &amp;lt;module&amp;gt;
    from streamlit.delta_generator import DeltaGenerator as _DeltaGenerator
  File "/path/to/python39-m1test/.venv/lib/python3.9/site-packages/streamlit/delta_generator.py", line 62, in &amp;lt;module&amp;gt;
    from streamlit.elements.image import ImageMixin
  File "/path/to/python39-m1test/.venv/lib/python3.9/site-packages/streamlit/elements/image.py", line 25, in &amp;lt;module&amp;gt;
    from PIL import Image, ImageFile
  File "/path/to/python39-m1test/.venv/lib/python3.9/site-packages/PIL/Image.py", line 89, in &amp;lt;module&amp;gt;
    from . import _imaging as core
ImportError: dlopen(/path/to/python39-m1test/.venv/lib/python3.9/site-packages/PIL/_imaging.cpython-39-darwin.so, 0x0002): symbol not found in flat namespace '_xcb_connect'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It seemed that the compiled binary in the PIL package was not working correctly in this platform.&lt;/p&gt;

&lt;p&gt;So I removed the virtual evn and the installed Python runtime once,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ rm -rf .venv

$ pyenv uninstall 3.9.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and installed the Python runtime for Intel chip with the command described above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ arch -arch x86_64 env PATH=${PATH/\/opt\/homebrew\/bin:/} pyenv install 3.9.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, I followed the same steps to set up venv and install the dependencies.&lt;/p&gt;

&lt;p&gt;Finally, everything worked correctly.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>5 mins talk about Streamlit and real-time video streaming at PyConJP 2021</title>
      <dc:creator>Yuichiro Tachibana (Tsuchiya)</dc:creator>
      <pubDate>Fri, 07 Jan 2022 13:00:00 +0000</pubDate>
      <link>https://dev.to/whitphx/5-mins-talk-about-streamlit-and-real-time-video-streaming-at-pyconjp-2021-5m</link>
      <guid>https://dev.to/whitphx/5-mins-talk-about-streamlit-and-real-time-video-streaming-at-pyconjp-2021-5m</guid>
      <description>&lt;p&gt;I had an opportunity to have a lightning talk at PyCon JP 2021&lt;sup id="fnref-1"&gt;1&lt;/sup&gt; and its video has been recorded and published 😃&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/_LuLs8H1gJc"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;In this LT, I demonstrated live coding of a real-time video app using Streamlit. Its content is also a part of &lt;a href="https://dev.to/whitphx/developing-web-based-real-time-videoaudio-processing-apps-quickly-with-streamlit-4k89"&gt;this post&lt;/a&gt;.&lt;br&gt;&lt;br&gt;
The LT also contains live demos of the example apps.&lt;/p&gt;

&lt;p&gt;I constructed the talk only with live coding and live demos and did not use any slides.&lt;br&gt;&lt;br&gt;
The first reason is simple; I thought it would be attractive. I think the audience does not listen to the words so much, but they are focused if there are visual/moving things.&lt;br&gt;&lt;br&gt;
The second is that I didn’t have enough time to prepare the talk because the presenters had been selected by lottery about 3 hours before the session. So I decided just to gather existing materials to make the presentation to minimize the amount of time I would waste if I was not selected.&lt;/p&gt;

&lt;p&gt;This was the first time for me to have an English talk in front of a public audience (even though it was online this time).&lt;br&gt;&lt;br&gt;
It was not perfect, but a good experience for me 😀 I look forward to the next chance.&lt;/p&gt;




&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://2021.pycon.jp/"&gt;https://2021.pycon.jp/&lt;/a&gt;↩
&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>python</category>
      <category>pycon</category>
      <category>streamlit</category>
      <category>conferences</category>
    </item>
    <item>
      <title>Developing web-based real-time video/audio processing apps quickly with Streamlit</title>
      <dc:creator>Yuichiro Tachibana (Tsuchiya)</dc:creator>
      <pubDate>Fri, 31 Dec 2021 13:00:00 +0000</pubDate>
      <link>https://dev.to/whitphx/developing-web-based-real-time-videoaudio-processing-apps-quickly-with-streamlit-4k89</link>
      <guid>https://dev.to/whitphx/developing-web-based-real-time-videoaudio-processing-apps-quickly-with-streamlit-4k89</guid>
      <description>&lt;h1&gt;
  
  
  Overview
&lt;/h1&gt;

&lt;p&gt;In this article, we will see how we can create browser-ready real-time video/audio processing apps with &lt;a href="https://streamlit.io/" rel="noopener noreferrer"&gt;Streamlit&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Streamlit is a Python framework with which developers can quickly build web apps without frontend coding. On top of it, developers can make real-time video/audio processing apps that receive video/audio streams from users’ media devices, only with ~10 lines of code in the case of the simplest example.&lt;/p&gt;

&lt;p&gt;Since such apps are web-based, they can be deployed to the cloud, shared with users easily, and have modern and user-friendly UIs.&lt;/p&gt;

&lt;p&gt;This tech stack is useful for creating demos and prototyping ideas of video/audio apps such as human or object detection, style transfer, image filters, speech recognition, video chat apps, and more.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz8m2henue8l7rwl4e2c0.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz8m2henue8l7rwl4e2c0.gif"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A sample web-based object detection app. Users can change the threshold interactively during execution. &lt;a href="https://share.streamlit.io/whitphx/streamlit-webrtc-example/main/app.py" rel="noopener noreferrer"&gt;Online demo🎈&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjj9d11g45b1oo1w12u4q.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjj9d11g45b1oo1w12u4q.gif"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A sample web-based style transfer app. Users can change model type and model parameters interactively during execution. &lt;a href="https://share.streamlit.io/whitphx/style-transfer-web-app/main/app.py" rel="noopener noreferrer"&gt;Online demo🎈&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can see more examples at the &lt;em&gt;examples&lt;/em&gt; section&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: These sample apps are hosted on the public cloud (&lt;a href="https://streamlit.io/cloud" rel="noopener noreferrer"&gt;Streamlit Cloud&lt;/a&gt;), and the video and audio streams are transmitted to and processed at the cloud server. While those data are only processed on memory and not saved to any storage, however, if you are concerned, please do not use them. As for the following contents in this article, we can execute all of them on our local. In addition, you can try the examples above on your local by following the instructions at the &lt;em&gt;examples&lt;/em&gt; section.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt;&lt;br&gt;
I had a presentation about this topic at &lt;a href="https://ep2022.europython.eu/" rel="noopener noreferrer"&gt;EuroPython 2022&lt;/a&gt; titled as &lt;a href="https://ep2022.europython.eu/session/real-time-browser-ready-computer-vision-apps-with-streamlit" rel="noopener noreferrer"&gt;"Real-time browser-ready computer vision apps with Streamlit."&lt;/a&gt;&lt;br&gt;
The talk video is available below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=uVD6B8WLMTo&amp;amp;t=16118s" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=uVD6B8WLMTo&amp;amp;t=16118s&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt;&lt;br&gt;
This article has been updated on 2022/09/02 using the newly introduced API of &lt;code&gt;streamlit-webrtc&lt;/code&gt; that has been available since &lt;a href="https://github.com/whitphx/streamlit-webrtc/blob/main/CHANGELOG.md#0400---2022-06-07" rel="noopener noreferrer"&gt;v0.40.0&lt;/a&gt;.&lt;br&gt;
The diff is &lt;a href="https://github.com/whitphx/whitphx.info/pull/17" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;
  
  
  The advantages of web-based apps
&lt;/h1&gt;

&lt;p&gt;We have been typically using OpenCV to build real-time demo apps of image or video processing. Some of you (especially developers or researchers in such fields) may have seen the following code or similar many times.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;

&lt;span class="n"&gt;cap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;VideoCapture&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="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;frame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Canny&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Some image processing
&lt;/span&gt;
    &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;imshow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;frame&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0xFF&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;ord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;q&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;

&lt;span class="n"&gt;cap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;release&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroyAllWindows&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compared to the GUI apps like above using &lt;code&gt;cv2.VideoCapture&lt;/code&gt; and &lt;code&gt;cv2.imshow&lt;/code&gt; that run on local environments, web-based apps have some advantages like below.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easy to share and run:

&lt;ul&gt;
&lt;li&gt;If we deploy the apps on the cloud, we can share the apps with our users simply by sending the URLs.&lt;/li&gt;
&lt;li&gt;The users can use the apps only by accessing them through web browsers. It does not require any set-ups or external dependencies.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Usable on smartphones

&lt;ul&gt;
&lt;li&gt;Because all the users need is web browsers, the users can use the apps on their smartphones. It’s convenient if we can show demos on such portable devices.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;User-friendly UIs.

&lt;ul&gt;
&lt;li&gt;Developers can use text inputs, sliders, or other web-based components to accept user inputs or show data. Such web-based UIs are more friendly for users than desktop GUIs in recent days.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h1&gt;
  
  
  Tutorial
&lt;/h1&gt;

&lt;p&gt;We will create a simple web-based real-time video processing app with ~10 or 20 LoC. Please try this tutorial in an environment where a webcam and a microphone are available.&lt;/p&gt;

&lt;p&gt;You can check the final result of this tutorial in &lt;a href="https://github.com/whitphx/streamlit-webrtc-article-tutorial-sample" rel="noopener noreferrer"&gt;this repository&lt;/a&gt;. The deployed online demo is &lt;a href="https://share.streamlit.io/whitphx/streamlit-webrtc-article-tutorial-sample/main/app.py" rel="noopener noreferrer"&gt;here🎈&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this tutorial, we will write code in &lt;code&gt;app.py&lt;/code&gt;. Please create an empty &lt;code&gt;app.py&lt;/code&gt; at first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Install necessary packages
&lt;/h2&gt;

&lt;p&gt;Next, we have to install the packages necessary for this tutorial.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-U&lt;/span&gt; streamlit streamlit-webrtc opencv-python-headless
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;streamlit&lt;/code&gt;: The Streamlit main package.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;streamlit-webrtc&lt;/code&gt;: A custom component of Streamlit which deals with real-time video and audio streams.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;opencv-python-headless&lt;/code&gt;: OpenCV. We choose the headless version here because we will construct the UI with Streamlit.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  First contact with Streamlit
&lt;/h2&gt;

&lt;p&gt;NOTE: Please skip this section if you have experience in Streamlit.&lt;/p&gt;

&lt;p&gt;First of all, launch the Streamlit with the command below. Please run the command in the same directory to the &lt;code&gt;app.py&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;streamlit run app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After a while, the Streamlit server process will boot up. Then access &lt;a href="http://localhost:8501" rel="noopener noreferrer"&gt;http://localhost:8501&lt;/a&gt; to see the page like below (or it will automatically open in the browser by default). The screenshot here is in dark-mode, and if you are using light-mode, it looks different.&lt;/p&gt;

&lt;p&gt;At this moment, there is no content on the web page because &lt;code&gt;app.py&lt;/code&gt; is empty. We will add lines of code in the &lt;code&gt;app.py&lt;/code&gt; for the Streamlit app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc80xtfk06cd7z1wib2pe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc80xtfk06cd7z1wib2pe.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;app.py&lt;/code&gt; with your editor and write the code below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;streamlit&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;

&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;My first Streamlit app&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello, world&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you save the file, Streamlit will detect the file change and shows the “Rerun” and “Always rerun” buttons on the top right of the screen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffu8eh57kjrwvtyuphiqt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffu8eh57kjrwvtyuphiqt.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the “Rerun” button. Then the web page is reloaded, and the page content would be like below. The web page content is generated based on the &lt;code&gt;app.py&lt;/code&gt; code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjfb3c90djdf5zvisgkhz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjfb3c90djdf5zvisgkhz.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you had clicked the “Always rerun” button, the page would automatically be reloaded every time the file changed.&lt;/p&gt;

&lt;p&gt;Note that you have to reload the page like above in the following instructions where you update the &lt;code&gt;app.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now we have walked through the basic development flow of Streamlit apps. You write Python code with Streamlit components like &lt;code&gt;st.title()&lt;/code&gt; and &lt;code&gt;st.write()&lt;/code&gt; and pass it to the &lt;code&gt;streamlit run&lt;/code&gt; command, then Streamlit generates the corresponding frontend contents on the web page.&lt;/p&gt;

&lt;p&gt;In the next section, we will see how to develop a real-time video processing app on top of Streamlit. Apart from that, Streamlit itself covers more use cases such as machine learning, data science, or more general purposes. For such use cases, please see &lt;a href="https://docs.streamlit.io/library/get-started/create-an-app" rel="noopener noreferrer"&gt;the official Streamlit tutorial&lt;/a&gt; for example.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduce the real-time video/audio streaming component
&lt;/h2&gt;

&lt;p&gt;Update the &lt;code&gt;app.py&lt;/code&gt; as below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;streamlit&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;streamlit_webrtc&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;webrtc_streamer&lt;/span&gt;

&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;My first Streamlit app&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello, world&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;webrtc_streamer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;example&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have added a single line with &lt;code&gt;webrtc_streamer()&lt;/code&gt;. The web app would be like the screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffzlvprz6x54eqig732ks.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffzlvprz6x54eqig732ks.png"&gt;&lt;/a&gt;&lt;br&gt;
At the first trial, it may take some time to compile the package so that the page keeps showing the “running” message for a while after clicking the “Rerun” button. In such a case, wait for the process to finish.&lt;/p&gt;

&lt;p&gt;Click the “START” button to start the video and audio streaming. You may be asked for permission to access the webcam and microphone at the first trial. Grant permission in that case. &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnitcn1y9jsvo9rsq8cin.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnitcn1y9jsvo9rsq8cin.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;webrtc_streamer(key="example")&lt;/code&gt; above is a Streamlit component which deals with video and audio real-time I/O through web browsers. The &lt;code&gt;key&lt;/code&gt; argument is a unique ID in the script to identify the component instance. We have set it as &lt;code&gt;"example"&lt;/code&gt; here, but you can use any string for it. The component in this example only receives video and audio from the client-side webcam and microphone and outputs the raw streams. It’s the most basic version of the component. We are going to enhance its functionality by adding other options in the following sections.&lt;/p&gt;
&lt;h2&gt;
  
  
  Development of a real-time video processing application
&lt;/h2&gt;

&lt;p&gt;Update the &lt;code&gt;app.py&lt;/code&gt; as follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;streamlit&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;streamlit_webrtc&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;webrtc_streamer&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;av&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;

&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;My first Streamlit app&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello, world&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_ndarray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bgr24&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cvtColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Canny&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;COLOR_GRAY2BGR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;av&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;VideoFrame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_ndarray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bgr24&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nf"&gt;webrtc_streamer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;example&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;video_frame_callback&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Try it out by clicking the “START” button like the previous section. With this new example, you can find that an image filter is applied to the video stream.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh6j9v6dmodgt3mhbswwj.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh6j9v6dmodgt3mhbswwj.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have defined a callback that receives an input frame and returns an output frame. We also put image processing (edge detection in this example) code inside the callback. As a result, we have injected the image processing code into the real-time video app through the callback.&lt;/p&gt;

&lt;p&gt;Detailed explanations about the code follow.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;webrtc_streamer()&lt;/code&gt; can take a function object through the &lt;code&gt;video_frame_callback&lt;/code&gt; argument as a callback.&lt;/li&gt;
&lt;li&gt;The callback receives and returns input and output image frames. These are instances of the &lt;a href="https://pyav.org/docs/develop/api/video.html#av.video.frame.VideoFrame" rel="noopener noreferrer"&gt;&lt;code&gt;VideoFrame&lt;/code&gt;&lt;/a&gt; class from &lt;a href="https://github.com/PyAV-Org/PyAV" rel="noopener noreferrer"&gt;&lt;code&gt;PyAV&lt;/code&gt;&lt;/a&gt;.

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;PyAV&lt;/code&gt; library is a Python binding of &lt;code&gt;ffmpeg&lt;/code&gt;, which provides video and audio capabilities. It is installed as a dependency of &lt;code&gt;streamlit-webrtc&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;The argument of the callback is an image frame in the input video stream sourced from the webcam. It can be converted into a NumPy array with &lt;code&gt;frame.to_ndarray()&lt;/code&gt;.&lt;/li&gt;

&lt;li&gt;The returned value from the callback is displayed on the screen. In the sample above, a new &lt;code&gt;VideoFrame&lt;/code&gt; object to be returned is generated from a NumPy array, with &lt;code&gt;av.VideoFrame.from_ndarray(img, format="bgr24")&lt;/code&gt;&lt;sup id="fnref-3"&gt;3&lt;/sup&gt;.&lt;/li&gt;

&lt;li&gt;Any code can be put inside the callback. In the example above, we have used an edge detection filter &lt;code&gt;cv2.Canny(img, 100, 200)&lt;/code&gt; (and a grayscale converter &lt;code&gt;cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)&lt;/code&gt;) as an example.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Now, we have created a browser-ready real-time video processing app! We used a simple Canny edge detector in this example, and you can replace it with any image processing code in your original app.&lt;/p&gt;

&lt;p&gt;If we use object detection or style transfer for that part, the app would be like the screenshots at the beginning of this article&lt;sup id="fnref-4"&gt;4&lt;/sup&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Receive user inputs
&lt;/h2&gt;

&lt;p&gt;Update the &lt;code&gt;app.py&lt;/code&gt; as below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;streamlit&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;streamlit_webrtc&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;webrtc_streamer&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;av&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;

&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;My first Streamlit app&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello, world&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;threshold1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Threshold1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;threshold2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Threshold2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_ndarray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bgr24&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cvtColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Canny&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;threshold1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;threshold2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;cv2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;COLOR_GRAY2BGR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;av&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;VideoFrame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_ndarray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bgr24&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nf"&gt;webrtc_streamer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;example&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;video_frame_callback&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then click the “START” button. You will find that there are 2 sliders in this example. You can modify the parameters of &lt;code&gt;cv2.Canny()&lt;/code&gt; through the sliders, even during execution in real time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpvbjdggji5yx36w9rfai.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpvbjdggji5yx36w9rfai.gif" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this update,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We added &lt;code&gt;threshold1&lt;/code&gt; and &lt;code&gt;threshold2&lt;/code&gt; variables.&lt;/li&gt;
&lt;li&gt;We added two slider components with &lt;code&gt;st.slider()&lt;/code&gt; and assigned their values to these variables.

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;st.slider()&lt;/code&gt; is a built-in component of Streamlit. Its official API reference is &lt;a href="https://docs.streamlit.io/library/api-reference/widgets/st.slider" rel="noopener noreferrer"&gt;https://docs.streamlit.io/library/api-reference/widgets/st.slider&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;We then passed these variables to &lt;code&gt;cv2.Canny()&lt;/code&gt; inside the callback as its parameters.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Now we have interactive inputs to control the real-time video filter!&lt;/p&gt;

&lt;h2&gt;
  
  
  Execution model of the callback and an important notice about it
&lt;/h2&gt;

&lt;p&gt;Unlike OpenCV, &lt;code&gt;streamlit-webrtc&lt;/code&gt; requires callbacks to process image and audio frames. This callback-based design is one major difference between OpenCV GUI and &lt;code&gt;streamlit-webrtc&lt;/code&gt;, and there are a few things you have to be aware of about it.&lt;/p&gt;

&lt;p&gt;Please note that the callback is executed in a forked thread different from the main thread where the Streamlit app code runs. It makes some restrictions as below.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;global&lt;/code&gt; keyword does not work as expected inside the callback.&lt;/li&gt;
&lt;li&gt;Streamlit methods such as &lt;code&gt;st.write()&lt;/code&gt; cannot be used inside the callback.&lt;/li&gt;
&lt;li&gt;Communications between inside and outside the callback must be thread-safe.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Deploy the app to the cloud
&lt;/h1&gt;

&lt;p&gt;We are going to make the web app available to everyone by deploying it to the cloud.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure WebRTC
&lt;/h2&gt;

&lt;p&gt;To deploy the app to the cloud, we have to add &lt;code&gt;rtc_configuration&lt;/code&gt; parameter to the &lt;code&gt;webrtc_streamer()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;webrtc_streamer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;example&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;video_frame_callback&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;rtc_configuration&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="c1"&gt;# Add this line
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;iceServers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;urls&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stun:stun.l.google.com:19302&lt;/span&gt;&lt;span class="sh"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration is necessary to establish the media streaming connection when the server is on a remote host.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;streamlit_webrtc&lt;/code&gt; uses WebRTC for its video and audio streaming. It has to access a “STUN server” in the global network for the remote peers (precisely, peers over the NATs) to establish WebRTC connections. While we don’t look at the details about STUN servers in this article, please google it with keywords such as STUN, TURN, or NAT traversal if interested.&lt;/p&gt;

&lt;p&gt;We configured the code to use a free STUN server provided by Google in the example above. You can also use any other available STUN servers&lt;sup id="fnref-5"&gt;5&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;The value of the &lt;code&gt;rtc_configuration&lt;/code&gt; argument will be passed to the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/RTCPeerConnection" rel="noopener noreferrer"&gt;&lt;code&gt;RTCPeerConnection&lt;/code&gt;&lt;/a&gt; constructor on the frontend.&lt;/p&gt;

&lt;h2&gt;
  
  
  HTTPS
&lt;/h2&gt;

&lt;p&gt;We have to serve web apps on remote hosts via HTTPS to use webcams or microphones.&lt;/p&gt;

&lt;p&gt;Not only the &lt;code&gt;webrtc_streamer()&lt;/code&gt; component we used here but also any frontend apps that access the client-side webcams or microphones use &lt;a href="https://developer.mozilla.org/ja/docs/Web/API/MediaDevices/getUserMedia" rel="noopener noreferrer"&gt;&lt;code&gt;MediaDevices.getUserMedia()&lt;/code&gt;&lt;/a&gt; API. This API does not work in an “insecure context.”&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia#privacy_and_security" rel="noopener noreferrer"&gt;document&lt;/a&gt; says&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A secure context is, in short, a page loaded using HTTPS or the &lt;code&gt;file:///&lt;/code&gt; URL scheme, or a page loaded from &lt;code&gt;localhost&lt;/code&gt;.&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia#privacy_and_security" rel="noopener noreferrer"&gt;MediaDevices.getUserMedia() - Privacy and security&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As a result, we need HTTPS to serve web apps on remote hosts which access the client-side webcams or microphones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Streamlit Cloud
&lt;/h2&gt;

&lt;p&gt;I recommend &lt;a href="https://streamlit.io/cloud" rel="noopener noreferrer"&gt;Streamlit Cloud&lt;/a&gt; for Streamlit app hosting. You can deploy the apps from GitHub repositories with a few clicks, and it automatically serves the apps via HTTPS. And Streamlit Cloud seems to provide better runtime than Heroku free-tier, while Streamlit Cloud provides a large deployment capacity for free.&lt;/p&gt;

&lt;p&gt;Please refer to &lt;a href="https://docs.streamlit.io/streamlit-cloud" rel="noopener noreferrer"&gt;the official document&lt;/a&gt; for its usage.&lt;/p&gt;

&lt;p&gt;I actually deployed the app we have seen in this article on Streamlit Cloud: &lt;a href="https://share.streamlit.io/whitphx/streamlit-webrtc-article-tutorial-sample/main/app.py" rel="noopener noreferrer"&gt;https://share.streamlit.io/whitphx/streamlit-webrtc-article-tutorial-sample/main/app.py&lt;/a&gt; .&lt;/p&gt;

&lt;p&gt;Its GitHub repository is &lt;a href="https://github.com/whitphx/streamlit-webrtc-article-tutorial-sample" rel="noopener noreferrer"&gt;https://github.com/whitphx/streamlit-webrtc-article-tutorial-sample&lt;/a&gt; .&lt;/p&gt;

&lt;p&gt;Note that &lt;code&gt;requirements.txt&lt;/code&gt; has been added to install the necessary dependencies (&lt;code&gt;streamlit-webrtc&lt;/code&gt; and &lt;code&gt;opencv-python-headless&lt;/code&gt;) in the Streamlit Cloud environment: &lt;a href="https://github.com/whitphx/streamlit-webrtc-article-tutorial-sample/blob/main/requirements.txt" rel="noopener noreferrer"&gt;https://github.com/whitphx/streamlit-webrtc-article-tutorial-sample/blob/main/requirements.txt&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Notice
&lt;/h1&gt;

&lt;p&gt;As written above, the video and audio streams sourced from the client devices are transmitted to and processed at the server.&lt;/p&gt;

&lt;p&gt;So, this library is not scalable and depends on network connectivity. You may think of it mainly for prototyping or demo purpose.&lt;/p&gt;

&lt;p&gt;You also have to consider hosting the apps in local networks if there are concerns about transmitting media to the remote cloud server.&lt;/p&gt;

&lt;h1&gt;
  
  
  Examples
&lt;/h1&gt;

&lt;p&gt;This section is a copy of the sample list at &lt;a href="https://github.com/whitphx/streamlit-webrtc" rel="noopener noreferrer"&gt;https://github.com/whitphx/streamlit-webrtc&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Showcase including following examples and more
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/whitphx/streamlit-webrtc-example" rel="noopener noreferrer"&gt;⚡️Repository&lt;/a&gt;, &lt;a href="https://share.streamlit.io/whitphx/streamlit-webrtc-example/main/app.py" rel="noopener noreferrer"&gt;🎈Online demo&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Object detection&lt;/li&gt;
&lt;li&gt;OpenCV filter&lt;/li&gt;
&lt;li&gt;Uni-directional video streaming&lt;/li&gt;
&lt;li&gt;Audio processing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can try out this sample app using the following commands on your local env.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;streamlit-webrtc opencv-python-headless matplotlib pydub
&lt;span class="nv"&gt;$ &lt;/span&gt;streamlit run https://raw.githubusercontent.com/whitphx/streamlit-webrtc-example/main/app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Real-time Speech-to-Text
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/whitphx/streamlit-stt-app" rel="noopener noreferrer"&gt;⚡️Repository&lt;/a&gt;, &lt;a href="https://share.streamlit.io/whitphx/streamlit-stt-app/main/app_deepspeech.py" rel="noopener noreferrer"&gt;🎈Online demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It converts your voice into text in real time. This app is self-contained; it does not depend on any external API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-time video style transfer
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/whitphx/style-transfer-web-app" rel="noopener noreferrer"&gt;⚡️Repository&lt;/a&gt;, &lt;a href="https://share.streamlit.io/whitphx/style-transfer-web-app/main/app.py" rel="noopener noreferrer"&gt;🎈Online demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It applies a wide variety of style transfer filters to real-time video streams.&lt;/p&gt;

&lt;h2&gt;
  
  
  Video chat
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/whitphx/streamlit-video-chat-example" rel="noopener noreferrer"&gt;⚡️Repository&lt;/a&gt;(Online demo not available)&lt;/p&gt;

&lt;p&gt;You can create video chat apps with ~100 lines of Python code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tokyo 2020 Pictogram
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/whitphx/Tokyo2020-Pictogram-using-MediaPipe" rel="noopener noreferrer"&gt;⚡️Repository&lt;/a&gt;: &lt;a href="https://share.streamlit.io/whitphx/tokyo2020-pictogram-using-mediapipe/streamlit-app" rel="noopener noreferrer"&gt;🎈Online demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://google.github.io/mediapipe/" rel="noopener noreferrer"&gt;MediaPipe&lt;/a&gt; is used for pose estimation.&lt;/p&gt;

&lt;h1&gt;
  
  
  What about audio?
&lt;/h1&gt;

&lt;p&gt;You can deal with audio streams in a similar way as video. If you define a callback function and pass it to the &lt;code&gt;audio_frame_callback&lt;/code&gt; argument, the callback will be executed with audio frames. In the case of audio, the input argument and the returned value of the callback are instances of &lt;a href="https://pyav.org/docs/develop/api/audio.html#module-av.audio.frame" rel="noopener noreferrer"&gt;the &lt;code&gt;AudioFrame&lt;/code&gt; class&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Please see the source code of &lt;a href="https://github.com/whitphx/streamlit-webrtc/blob/c172483efd4566b18d3500e914285079117b5b35/pages/audio_filter.py" rel="noopener noreferrer"&gt;a sample app changing the audio gain&lt;/a&gt; or the Speech-to-Text app in the examples above.&lt;/p&gt;




&lt;ol&gt;
&lt;li&gt;Some experienced readers have noticed that we have not set the timing information of each frame. &lt;code&gt;streamlit-webrtc&lt;/code&gt; automatically sets such info to each frame when returned from the callback. It is also possible for the developers to set it manually, and &lt;code&gt;streamlit-webrtc&lt;/code&gt; does nothing in that case.↩
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/whitphx/streamlit-webrtc/blob/d6ce5b51e6c367a2e9488b8a50bfc652fee3b936/app.py#L309-L451" rel="noopener noreferrer"&gt;The source code of the object detection app&lt;/a&gt; and &lt;a href="https://github.com/whitphx/style-transfer-web-app/blob/2c835d11010b8d5c9acc8c8d681c9dfd0687b2ac/input.py#L37-L96" rel="noopener noreferrer"&gt;the style transfer app&lt;/a&gt;.↩
&lt;/li&gt;
&lt;li&gt;I once received a report that it took so long to make a connection with the library. The reason was that the reporter used Google’s STUN server from the Chinese network. It was solved by changing the server. &lt;a href="https://github.com/whitphx/streamlit-webrtc/issues/283#issuecomment-889753789" rel="noopener noreferrer"&gt;https://github.com/whitphx/streamlit-webrtc/issues/283#issuecomment-889753789&lt;/a&gt;↩
&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>streamlit</category>
      <category>computervision</category>
      <category>python</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>How to deploy Streamlit apps to Google App Engine</title>
      <dc:creator>Yuichiro Tachibana (Tsuchiya)</dc:creator>
      <pubDate>Sun, 12 Dec 2021 13:51:12 +0000</pubDate>
      <link>https://dev.to/whitphx/how-to-deploy-streamlit-apps-to-google-app-engine-407o</link>
      <guid>https://dev.to/whitphx/how-to-deploy-streamlit-apps-to-google-app-engine-407o</guid>
      <description>&lt;h1&gt;
  
  
  Things to know
&lt;/h1&gt;

&lt;p&gt;There are some things to know when you deploy Streamlit apps to App Engine.&lt;/p&gt;

&lt;h2&gt;
  
  
  The flexible environment is mandatory
&lt;/h2&gt;

&lt;p&gt;You have to choose the flexible environment because it supports WebSockets and the standard environment does not.&lt;br&gt;
Streamlit heavily relies on WebSockets for the communication between the server and the client.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F70wl8qyijy53qhu7zyjq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F70wl8qyijy53qhu7zyjq.png" alt="App Engine environment comparison on WebSockets support"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is a screenshot of &lt;a href="https://cloud.google.com/appengine/docs/the-appengine-environments" rel="noopener noreferrer"&gt;https://cloud.google.com/appengine/docs/the-appengine-environments&lt;/a&gt; on 2021/12/12. The left is about the standard env and the right is the flexible env.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Custom runtime is not necessary
&lt;/h2&gt;

&lt;p&gt;You &lt;strong&gt;do not have to&lt;/strong&gt; use a custom runtime (a customized Docker image).&lt;br&gt;
The flexible environment offers an official Python runtime as &lt;a href="https://cloud.google.com/appengine/docs/flexible/python/runtime" rel="noopener noreferrer"&gt;https://cloud.google.com/appengine/docs/flexible/python/runtime&lt;/a&gt; and you can use it.&lt;/p&gt;

&lt;p&gt;You can also use a custom runtime. For example, it is a nice option when you want to use a different Python version from the one provided as the official runtime or when you already have a working Docker image.&lt;/p&gt;

&lt;p&gt;NOTE:&lt;br&gt;
As of 2021/12/12, &lt;a href="https://cloud.google.com/appengine/docs/flexible/python/runtime" rel="noopener noreferrer"&gt;the document&lt;/a&gt; says the Python version on the built-in Python3 runtime is &lt;code&gt;3.7.2&lt;/code&gt;, but it is actually &lt;code&gt;3.6.10&lt;/code&gt; known from &lt;code&gt;sys.version&lt;/code&gt; at least in the &lt;code&gt;asia-northeast1&lt;/code&gt; zone (Tokyo).&lt;br&gt;
And Streamlit officially supports only Python&amp;gt;=3.7 while it is technically possible to be installed with Python 3.6, so maybe you should set up a custom runtime with Python&amp;gt;=3.7 following the section below.&lt;/p&gt;
&lt;h2&gt;
  
  
  The number of instances should be 1 if &lt;code&gt;st.file_uploader&lt;/code&gt; or &lt;code&gt;st.download_button&lt;/code&gt; is used
&lt;/h2&gt;

&lt;p&gt;If your app contains &lt;code&gt;st.file_uploader&lt;/code&gt; or &lt;code&gt;st.download_button&lt;/code&gt;, you should set the maximum number of instances to 1. For that configuration, see &lt;a href="https://cloud.google.com/appengine/docs/flexible/python/reference/app-yaml#services" rel="noopener noreferrer"&gt;https://cloud.google.com/appengine/docs/flexible/python/reference/app-yaml#services&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;App Engine typically distributes the requests evenly among available instances so the file upload/download requests sometimes reach the instance different from the one where the session exists when there are multiple instances.&lt;/p&gt;

&lt;p&gt;Errors as below appear in such cases.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpvhja6ifm1mkvdb1y6e4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpvhja6ifm1mkvdb1y6e4.png" alt="Upload error"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;When an error occurs with file upload: The file upload request reaches a server where the session does not exist and the server returns the 400 error code.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7dhyuat74vpz3ux0ixdd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7dhyuat74vpz3ux0ixdd.png" alt="Download error"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;When an error occurs with file download: The file download request reaches a server where the session does not exist and the 404 response is returned.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This problem occurs with the file uploader/downloader components because they use normal stateless HTTP POST/GET requests while other components work on top of WebSocket connections consistent over sessions.&lt;/p&gt;

&lt;p&gt;While I know this problem occurs at least with these components, I'm not sure whether there is another component which have this problem. I have not checked all.&lt;/p&gt;

&lt;p&gt;NOTE:&lt;br&gt;
Restricting the number of instances to 1 has a drawback as it may cause some downtimes.&lt;br&gt;
&lt;a href="https://cloud.google.com/appengine/docs/flexible/python/how-instances-are-managed" rel="noopener noreferrer"&gt;As the document says&lt;/a&gt;, the flexible instances are restarted once a week, which leads to downtime when there are not multiple instances.&lt;br&gt;
While this problem has already been stated in the following posts, the only solution suggested was to set the minimum number of instances as more than one, although it conflicts with the solution explained in this article. I could not find a solution that covers both problems.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/46758419/do-app-engine-flexible-environment-vm-instance-restarts-take-advantage-of-automa" rel="noopener noreferrer"&gt;Do App Engine Flexible Environment VM instance restarts take advantage of automatic scaling? (Stack Overflow)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/53047941/rolling-restarts-are-causing-are-app-engine-app-to-go-offline-is-there-a-way-to" rel="noopener noreferrer"&gt;Rolling restarts are causing are app engine app to go offline. Is there a way to change the config to prevent that from happening? (Stack Overflow)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;NOTE:&lt;br&gt;
App Engine offers the &lt;a href="https://cloud.google.com/appengine/docs/flexible/ruby/using-websockets-and-session-affinity#session_affinity" rel="noopener noreferrer"&gt;session affinity&lt;/a&gt; setting, but it does not help in this case because it is only for HTTP long polling like &lt;code&gt;socket.io&lt;/code&gt; as &lt;a href="https://cloud.google.com/appengine/docs/flexible/ruby/using-websockets-and-session-affinity#session_affinity" rel="noopener noreferrer"&gt;documented&lt;/a&gt; though this problem is due to a different reason.&lt;/p&gt;
&lt;h1&gt;
  
  
  Recipes
&lt;/h1&gt;

&lt;p&gt;I will show some examples deploying Streamlit apps to App Engine in different situations.&lt;/p&gt;

&lt;p&gt;Each of them can be deployed with the command below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;gcloud app deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All sample resources are available at &lt;a href="https://github.com/whitphx/streamlit-appengine-samples" rel="noopener noreferrer"&gt;https://github.com/whitphx/streamlit-appengine-samples&lt;/a&gt; .&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic setup
&lt;/h2&gt;

&lt;p&gt;This is the simplest one.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No custom runtime&lt;/li&gt;
&lt;li&gt;No config on the number of instances (scaling)

&lt;ul&gt;
&lt;li&gt;This app is not using file uploader or downloader.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/whitphx/streamlit-appengine-samples/tree/main/helloworld" rel="noopener noreferrer"&gt;https://github.com/whitphx/streamlit-appengine-samples/tree/main/helloworld&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  File list
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── app.yaml
├── requirements.txt
└── streamlit-app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;app.yaml&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python&lt;/span&gt;
&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;flex&lt;/span&gt;

&lt;span class="na"&gt;runtime_config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;python_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;

&lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;streamlit run streamlit-app.py --server.port $PORT&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The flexible environment is selected.&lt;/li&gt;
&lt;li&gt;The built-in Python3 runtime is used.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;entrypoint&lt;/code&gt; is configured to run the Streamlit process with the specified port number via the &lt;code&gt;$PORT&lt;/code&gt; environment variable.

&lt;ul&gt;
&lt;li&gt;See &lt;a href="https://cloud.google.com/appengine/docs/flexible/python/runtime#application_startup" rel="noopener noreferrer"&gt;https://cloud.google.com/appengine/docs/flexible/python/runtime#application_startup&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;requirements.txt&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;streamlit~=1.2.0

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;streamlit-app.py&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;streamlit&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;

&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;App Engine sample app&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text_input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Your name?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;world&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  File uploader and downloader
&lt;/h1&gt;

&lt;p&gt;This is a sample with a file uploader and a downloader.&lt;/p&gt;

&lt;h2&gt;
  
  
  File list
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── app.yaml
├── requirements.txt
└── streamlit-app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;app.yaml&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python&lt;/span&gt;
&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;flex&lt;/span&gt;

&lt;span class="na"&gt;runtime_config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;python_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;

&lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;streamlit run streamlit-app.py --server.port $PORT&lt;/span&gt;

&lt;span class="na"&gt;automatic_scaling&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;max_num_instances&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;

&lt;span class="c1"&gt;# Or manual scaling as below:&lt;/span&gt;
&lt;span class="c1"&gt;# manual_scaling:&lt;/span&gt;
&lt;span class="c1"&gt;#   instances: 1&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In addition to the basic set-up, &lt;code&gt;automatic_scaling.max_num_instances&lt;/code&gt; is set to &lt;code&gt;1&lt;/code&gt;.&lt;br&gt;
If you want to use the manual scaling, use &lt;code&gt;manual_scaling.instances&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;For these settings, see &lt;a href="https://cloud.google.com/appengine/docs/flexible/python/reference/app-yaml#services" rel="noopener noreferrer"&gt;https://cloud.google.com/appengine/docs/flexible/python/reference/app-yaml#services&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;code&gt;requirements.txt&lt;/code&gt;
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;streamlit~=1.2.0

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;streamlit-app.py&lt;/code&gt;
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;streamlit&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;

&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;App Engine sample app&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;uploaded_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;file_uploader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Upload some file&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;uploaded_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;uploaded_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; was uploaded.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;download_button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Download &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;uploaded_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;uploaded_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;uploaded_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h1&gt;
  
  
  Custom runtime
&lt;/h1&gt;

&lt;p&gt;This sample uses a custom runtime with &lt;code&gt;Dockerfile&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Ref: &lt;a href="https://cloud.google.com/appengine/docs/flexible/custom-runtimes/build" rel="noopener noreferrer"&gt;https://cloud.google.com/appengine/docs/flexible/custom-runtimes/build&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── Dockerfile
├── app.yaml
├── requirements.txt
└── streamlit-app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;app.yaml&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;custom&lt;/span&gt;
&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;flex&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Set &lt;code&gt;runtime: custom&lt;/code&gt; to use a custom runtime.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;entrypoint&lt;/code&gt; is not needed as it is defined in &lt;code&gt;Dockerfile&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Dockerfile&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; gcr.io/google-appengine/python&lt;/span&gt;

&lt;span class="c"&gt;# Ref:&lt;/span&gt;
&lt;span class="c"&gt;# * https://github.com/GoogleCloudPlatform/python-runtime/blob/8cdc91a88cd67501ee5190c934c786a7e91e13f1/README.md#kubernetes-engine--other-docker-hosts&lt;/span&gt;
&lt;span class="c"&gt;# * https://github.com/GoogleCloudPlatform/python-runtime/blob/8cdc91a88cd67501ee5190c934c786a7e91e13f1/scripts/testdata/hello_world_golden/Dockerfile&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;virtualenv /env &lt;span class="nt"&gt;-p&lt;/span&gt; python3.7

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; VIRTUAL_ENV /env&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PATH /env/bin:$PATH&lt;/span&gt;

&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="s"&gt; requirements.txt /app/&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="s"&gt; . /app&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; [ "streamlit", "run", "streamlit-app.py", "--server.port", "8080" ]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Use the base image officially provided by Google for App Engine, &lt;code&gt;gcr.io/google-appengine/python&lt;/code&gt;.

&lt;ul&gt;
&lt;li&gt;App Engine has a health checking mechanism and the official base image is already configured for it.&lt;/li&gt;
&lt;li&gt;See &lt;a href="https://cloud.google.com/appengine/docs/flexible/python/customizing-the-python-runtime" rel="noopener noreferrer"&gt;https://cloud.google.com/appengine/docs/flexible/python/customizing-the-python-runtime&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Specify the Python version at virtualenv creation.

&lt;ul&gt;
&lt;li&gt;Follow &lt;a href="https://github.com/GoogleCloudPlatform/python-runtime/blob/8cdc91a88cd67501ee5190c934c786a7e91e13f1/README.md#kubernetes-engine--other-docker-hosts" rel="noopener noreferrer"&gt;the README.md on &lt;code&gt;GoogleCloudPlatform/python-runtime&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/GoogleCloudPlatform/python-runtime/blob/8cdc91a88cd67501ee5190c934c786a7e91e13f1/scripts/testdata/hello_world_golden/Dockerfile" rel="noopener noreferrer"&gt;Some &lt;code&gt;Dockerfile&lt;/code&gt;s of the test data in that repository&lt;/a&gt; can be references&lt;/li&gt;

&lt;li&gt;The main process has to listen to the port 8080 in a custom runtime.

&lt;ul&gt;
&lt;li&gt;See &lt;a href="https://cloud.google.com/appengine/docs/flexible/custom-runtimes/build#listening_to_port_8080" rel="noopener noreferrer"&gt;https://cloud.google.com/appengine/docs/flexible/custom-runtimes/build#listening_to_port_8080&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;requirements.txt&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;streamlit~=1.2.0

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;streamlit-app.py&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;streamlit&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;


&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>python</category>
      <category>streamlit</category>
      <category>googlecloud</category>
      <category>gcp</category>
    </item>
  </channel>
</rss>
