<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Daren Liang</title><link>/</link><description>Recent content on Daren Liang</description><generator>Hugo</generator><language>en</language><lastBuildDate>Mon, 11 Aug 2025 00:00:00 +0000</lastBuildDate><atom:link href="/index.xml" rel="self" type="application/rss+xml"/><item><title>Examining Parlays in Prediction Markets</title><link>/posts/stat-arb-on-polymarket/</link><pubDate>Mon, 11 Aug 2025 00:00:00 +0000</pubDate><guid>/posts/stat-arb-on-polymarket/</guid><description>&lt;p&gt;It&amp;rsquo;s been a while since my last post.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;d like to share a quick post on something I&amp;rsquo;ve been noticing on prediction
markets like &lt;a href="https://polymarket.com/"&gt;Polymarket&lt;/a&gt; regarding parlay contracts.&lt;/p&gt;
&lt;h2 id="overview-of-prediction-markets"&gt;Overview of Prediction Markets&lt;/h2&gt;
&lt;p&gt;For those who don&amp;rsquo;t know, here&amp;rsquo;s a quick primer on how prediction markets work:&lt;/p&gt;
&lt;h3 id="a-prediction-market-is-an-information-market"&gt;A Prediction Market is an &amp;ldquo;Information Market&amp;rdquo;&lt;/h3&gt;
&lt;p&gt;A prediction market, similar to an option market, is a platform where
participants buy and sell contracts that pay out based on the outcome of
future events. Rather than paying out based on the price of an underlying
asset, like a stock option, these contracts pay out based on specific events,
such as the outcome of an election or a sports game. Beyond financial
incentives, prediction markets aggregate information from many participants to
produce a consensus probability for future events.&lt;/p&gt;</description></item><item><title>Solving Dice Problems with Markov Chains</title><link>/posts/absorbing-markov-chains/</link><pubDate>Sat, 28 Sep 2024 00:00:00 +0000</pubDate><guid>/posts/absorbing-markov-chains/</guid><description>&lt;p&gt;A colleague asked me an interesting probability problem involving dice. If you roll a die
continuously, are you more likely to roll two consecutive 5&amp;rsquo;s or a 5 immediately followed by a 6?
On average how many rolls are required to roll two consecutive 5&amp;rsquo;s?
How about rolling 5 immediately followed by a 6?&lt;/p&gt;
&lt;div class="boxed"&gt;
&lt;h3&gt;Interactive Example&lt;/h3&gt;
&lt;div style="margin: 10px 0"&gt;
 &lt;button onclick="roll55()"&gt;Roll 5,5&lt;/button&gt; &lt;button onclick="roll56()"&gt;Roll 5,6&lt;/button&gt;
&lt;/div&gt;
&lt;div style="margin: 10px 0"&gt;
&lt;p id="rolls" style="word-break: break-all"&gt;&lt;br&gt;&lt;/p&gt;
&lt;p id="msg"&gt;&lt;br&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 id="solution"&gt;Solution&lt;/h3&gt;
&lt;details&gt;
&lt;summary&gt;Reveal the Solution&lt;/summary&gt;
&lt;p&gt;The state machine diagram for rolling two consecutive 5&amp;rsquo;s:&lt;/p&gt;</description></item><item><title>A New Way to Write Microservice Code</title><link>/posts/a-new-way-to-write-microservice-code/</link><pubDate>Sun, 28 Jan 2024 00:00:00 +0000</pubDate><guid>/posts/a-new-way-to-write-microservice-code/</guid><description>&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Microservices"&gt;Microservices&lt;/a&gt; are getting really
popular, but have we reached the pinnacle of microservice design? I&amp;rsquo;ve been
imagining a new way to write microservice code where you can annotate functions
you want to be microservices. That way, you get the microservice architecture
&lt;em&gt;&lt;strong&gt;directly&lt;/strong&gt;&lt;/em&gt; baked into the code.&lt;/p&gt;
&lt;p&gt;Sounds a bit like the new hit buzzword &amp;ldquo;Infrastructure as code&amp;rdquo; that everyone
is using.&lt;/p&gt;
&lt;h3 id="selling-the-idea"&gt;Selling the idea&lt;/h3&gt;
&lt;p&gt;The concept is simple. You can decorate functions that you want to be
highly available, highly durable, highly reliable, highly resilient,
highly fault-tolerant, or anything else scalability preachers might want.&lt;/p&gt;</description></item><item><title>Dealer Router Pattern in NNG</title><link>/posts/dealer-router-pattern-in-nng/</link><pubDate>Fri, 12 Jan 2024 00:00:00 +0000</pubDate><guid>/posts/dealer-router-pattern-in-nng/</guid><description>&lt;p&gt;&lt;a href="https://nng.nanomsg.org/"&gt;NNG&lt;/a&gt; is a broker-less message queue that is a
lightweight alternative to &lt;a href="https://zeromq.org/"&gt;ZeroMQ&lt;/a&gt; and supersedes the
older &lt;a href="https://nanomsg.org/"&gt;nanomsg&lt;/a&gt; project. It offers a
more &amp;ldquo;&lt;a href="https://en.wikipedia.org/wiki/Orthogonality_(programming)"&gt;orthogonal&lt;/a&gt;&amp;rdquo;
API and does a few things better than ZeroMQ.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Thread-safe sockets&lt;/li&gt;
&lt;li&gt;Websocket transport support&lt;/li&gt;
&lt;li&gt;POSIX-compliant sockets API&lt;/li&gt;
&lt;li&gt;True zero-copy messaging&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A common messaging pattern is Dealer-Router which can be easily implemented
using ZeroMQ&amp;rsquo;s DEALER and ROUTER sockets. Unfortunately, NNG does not have such
sockets and implementing the pattern is not
&lt;a href="https://github.com/nanomsg/nng/issues/781"&gt;trivial&lt;/a&gt;. Fortunately, using NNG&amp;rsquo;s
raw mode sockets we can emulate Dealer-Router.&lt;/p&gt;</description></item><item><title>Problems with Proxying Images</title><link>/posts/problems-with-proxing-images/</link><pubDate>Sat, 27 Aug 2022 00:00:00 +0000</pubDate><guid>/posts/problems-with-proxing-images/</guid><description>&lt;p&gt;Let&amp;rsquo;s face it. Serving images on the web sucks. You have to manage endless
formats, dimensions, variants, and more. What are some of the solutions we have
at the moment?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://imgproxy.net"&gt;&lt;strong&gt;imgproxy&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://imgix.com"&gt;&lt;strong&gt;imgix&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://imagekit.io"&gt;&lt;strong&gt;ImageKit&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://optimole.com"&gt;&lt;strong&gt;Optimole&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&amp;rsquo;s discuss some flaws that affect each of these services.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;imgproxy&lt;/strong&gt; is a self-hosted solution for proxying images. The core is open
source but advanced features are paid. One major downside is that you must pay
for concurrency, making it not suitable for real-time image transformations.
Moreover, it can be expensive to self-host multiple image proxies, making it
hard to deploy image proxies to the edge, increasing latency to your users.&lt;/p&gt;</description></item><item><title>The Basketball Tryout</title><link>/posts/the-basketball-tryout/</link><pubDate>Fri, 13 May 2022 00:00:00 +0000</pubDate><guid>/posts/the-basketball-tryout/</guid><description>&lt;p&gt;Here&amp;rsquo;s another interesting probability question that I&amp;rsquo;ve found in Adrian
Torchiana&amp;rsquo;s &lt;a href="https://github.com/atorch/probability_puzzles"&gt;Probability Math Puzzles&lt;/a&gt; app.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You&amp;rsquo;re trying out for a basketball team, and the coach gives you two options: you can take six shots, or you can take
four. In both cases you have to make at least half of the shots, or you&amp;rsquo;ll be cut from the team.&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Each shot you take has a fixed probability of succeeding. What is the probability of making a successful shot, in
which it makes no difference what option you choose?&lt;/p&gt;</description></item><item><title>FUSEing for Fun</title><link>/posts/fuseing-for-fun/</link><pubDate>Sat, 26 Mar 2022 00:00:00 +0000</pubDate><guid>/posts/fuseing-for-fun/</guid><description>&lt;script async src="https://buttons.github.io/buttons.js"&gt;&lt;/script&gt;
&lt;p&gt;Something that I find absolutely fascinating
are &lt;a href="https://en.wikipedia.org/wiki/Filesystem_in_Userspace"&gt;Filesystems in USErspace (FUSE)&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="what-is-fuse"&gt;What is FUSE?&lt;/h3&gt;
&lt;p&gt;FUSE, to put it simply, allows people to implement filesystems in userspace. Filesystems are traditionally implemented
in the kernel, but when using FUSE, non-privileged programs can be used to create custom filesystems.&lt;/p&gt;
&lt;h3 id="why-is-it-cool"&gt;Why is it cool?&lt;/h3&gt;
&lt;p&gt;Filesystems don&amp;rsquo;t need to be concrete! You can implement virtual filesystems with little or no limitations.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Don&amp;rsquo;t want files to write to disk? Implement a &amp;ldquo;ramdisk&amp;rdquo; in FUSE.&lt;/li&gt;
&lt;li&gt;Want to use Amazon S3 as if it is a hard drive on your computer?
Use &lt;a href="https://github.com/s3fs-fuse/s3fs-fuse"&gt;s3fs-fuse&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Want to be evil 😈 and store files on Google sheets? Use
&lt;a href="https://github.com/GunshipPenguin/spreadsheetfs"&gt;spreadsheetfs&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="introducing-dsfs"&gt;Introducing dsfs&lt;/h2&gt;
&lt;p&gt;Dsfs is an experimental filesystem written in Go and backed by Discord attachments.&lt;/p&gt;</description></item><item><title>Generating Images with Serverless</title><link>/posts/generating-images-with-serverless/</link><pubDate>Fri, 18 Feb 2022 00:00:00 +0000</pubDate><guid>/posts/generating-images-with-serverless/</guid><description>&lt;p&gt;One thing I&amp;rsquo;ve always wanted to do was write a serverless function that could generate
&lt;a href="https://counter-strike.net/"&gt;CS:GO&lt;/a&gt; crosshair images. The serverless platform that I chose
is &lt;a href="https://workers.cloudflare.com/"&gt;Cloudflare Workers&lt;/a&gt;
which utilizes a vast edge network and
supports &lt;a href="https://blog.cloudflare.com/eliminating-cold-starts-with-cloudflare-workers/"&gt;0 ms cold starts&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Writing a serverless function for this task didn&amp;rsquo;t seem too difficult, but I quickly ran into some issues.&lt;/p&gt;
&lt;h2 id="using-node-canvas"&gt;Using node-canvas&lt;/h2&gt;
&lt;p&gt;Modern browsers are equipped with the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API"&gt;Canvas API&lt;/a&gt; which
makes it extremely easy to create 2D graphics. Although this API is meant to be used in the browser, Automattic
(the maker of WordPress) has an implementation of Canvas for Node.js
called &lt;a href="https://github.com/Automattic/node-canvas"&gt;node-canvas&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>The Gambler's Ruin</title><link>/posts/the-gamblers-ruin/</link><pubDate>Wed, 26 Jan 2022 00:00:00 +0000</pubDate><guid>/posts/the-gamblers-ruin/</guid><description>&lt;p&gt;Here&amp;rsquo;s an interesting probability question that I&amp;rsquo;ve found in Adrian
Torchiana&amp;rsquo;s &lt;a href="https://github.com/atorch/probability_puzzles"&gt;Probability Math Puzzles&lt;/a&gt; app (highly recommended!) back in
high school.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You find $2 in your pocket and decide to go gambling. Fortunately, the game you&amp;rsquo;re playing has very favorable odds: each time you play, you gain $1 with probability 3/4 and lose $1 with probability 1/4.&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Suppose you continue playing so long as you have money in your pocket. If you lose your first two bets, you go broke and go home after only two rounds; but if you win forever, you&amp;rsquo;ll play forever.&lt;/p&gt;</description></item><item><title>Secret Sharing by Example</title><link>/posts/secret-sharing-by-example/</link><pubDate>Thu, 20 Jan 2022 00:00:00 +0000</pubDate><guid>/posts/secret-sharing-by-example/</guid><description>&lt;h2 id="1-the-scenario"&gt;1. The Scenario&lt;/h2&gt;
&lt;p&gt;You are haphazardly appointed as the group in charge of authorizing the launch of your country&amp;rsquo;s nuclear weapons. No
idea how you got appointed, maybe you were just unlucky &amp;#x1f937;&amp;zwj;&amp;#x2642;&amp;#xfe0f;.&lt;/p&gt;
&lt;p&gt;Knowing that you are tasked with managing the launch code, you&amp;rsquo;ve decided that the only way to ensure no one abuses this
code is to share the code in such a way that you and your friends must come together to reveal the secret code.&lt;/p&gt;</description></item><item><title>Algorithms in Javagony</title><link>/posts/javagony/</link><pubDate>Tue, 08 Oct 2019 00:00:00 +0000</pubDate><guid>/posts/javagony/</guid><description>&lt;script async src="https://buttons.github.io/buttons.js"&gt;&lt;/script&gt;
&lt;h3 id="the-javagony"&gt;The Javagony&lt;/h3&gt;
&lt;p&gt;First coined by &lt;a href="https://chat.stackexchange.com/users/122082/flawr"&gt;flawr&lt;/a&gt;,
Javagony is Java with a few statements made not available.&lt;/p&gt;
&lt;p&gt;Here are the statements that are not available:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff79c6"&gt;for&lt;/span&gt; (){}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff79c6"&gt;if&lt;/span&gt; (){} &lt;span style="color:#ff79c6"&gt;else&lt;/span&gt; &lt;span style="color:#ff79c6"&gt;if&lt;/span&gt; (){} &lt;span style="color:#ff79c6"&gt;else&lt;/span&gt; {}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff79c6"&gt;while&lt;/span&gt; (){}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff79c6"&gt;do&lt;/span&gt; {} &lt;span style="color:#ff79c6"&gt;while&lt;/span&gt; ()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff79c6"&gt;switch&lt;/span&gt; (){}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff79c6"&gt;?&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;These statements can be substituted for try catch statements and recursion.&lt;/p&gt;
&lt;p&gt;Although Javagony is not the least bit fun to write, it makes you think about
clever tricks that utilize many of Java&amp;rsquo;s obscure features.&lt;/p&gt;</description></item><item><title>PolymarketGPT: Event Oracle</title><link>/demos/polymarketgpt-event-oracle/</link><pubDate>Sat, 01 Jan 0011 00:00:00 +0000</pubDate><guid>/demos/polymarketgpt-event-oracle/</guid><description>&lt;p&gt;Ask a question about current or past events! This demo is powered
by &lt;a href="https://polymarket.com/"&gt;Polymarket&lt;/a&gt; and &lt;a href="https://qdrant.tech/"&gt;Qdrant&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Example questions:&lt;/p&gt;
&lt;ul&gt;
 &lt;li&gt;&lt;a href="javascript:void(0);" onclick="document.getElementById('query').value = 'Will Gavin Newsom win the 2028 US Presidential Election?'; execute()"&gt;Will Gavin Newsom win the 2028 US Presidential Election?&lt;/a&gt;&lt;/li&gt;
 &lt;li&gt;&lt;a href="javascript:void(0);" onclick="document.getElementById('query').value = 'Will the Fed cut rates in 2026?'; execute()"&gt;Will the Fed cut rates in 2026?&lt;/a&gt;&lt;/li&gt;
 &lt;li&gt;&lt;a href="javascript:void(0);" onclick="document.getElementById('query').value = 'What will SpaceX\'s public ticker be?'; execute()"&gt;What will SpaceX's public ticker be?&lt;/a&gt;&lt;/li&gt;
 &lt;li&gt;&lt;a href="javascript:void(0);" onclick="document.getElementById('query').value = 'Which company has the best AI model in 2026?'; execute()"&gt;Who currently has the best AI model in 2026?&lt;/a&gt;&lt;/li&gt;
 &lt;li&gt;&lt;a href="javascript:void(0);" onclick="document.getElementById('query').value = 'Which company will become the largest end of 2027?'; execute()"&gt;Which company will become the largest end of 2027?&lt;/a&gt;&lt;/li&gt;
 &lt;li&gt;&lt;a href="javascript:void(0);" onclick="document.getElementById('query').value = 'What is the US economy outlook in 2026?'; execute()"&gt;What is the US economy outlook in 2026?&lt;/a&gt;&lt;/li&gt;
 &lt;li&gt;&lt;a href="javascript:void(0);" onclick="document.getElementById('query').value = 'Which AI companies will Apple acquire?'; execute()"&gt;Which AI companies will Apple acquire?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;textarea id="query" rows="4" style="width: 100%; font-family: 'Space mono';" onkeypress="handle(event)"&gt;&lt;/textarea&gt;
&lt;p&gt;&lt;button onclick="execute()"&gt;Go&lt;/button&gt; (Ctrl/Cmd+Enter)&lt;/p&gt;</description></item><item><title>Query Financial Statements</title><link>/demos/query-financials/</link><pubDate>Fri, 01 Jan 0010 00:00:00 +0000</pubDate><guid>/demos/query-financials/</guid><description>&lt;p&gt;All queries are executed in the browser
using &lt;a href="https://duckdb.org/docs/api/wasm/overview.html"&gt;DuckDB WASM&lt;/a&gt; on
all &lt;a href="https://www.sec.gov/data-research/sec-markets-data/financial-statement-data-sets"&gt;10-Qs since January 2009&lt;/a&gt;.&lt;/p&gt;
&lt;p id="loader"&gt;Loading dataset&lt;/p&gt;
&lt;h2 id="get-financials-for-a-period"&gt;Get financials for a period&lt;/h2&gt;
&lt;div&gt;
Ticker: &lt;input type="text" list="tickerList" id="ticker" /&gt;
&lt;datalist id="tickerList"&gt;&lt;/datalist&gt;
&lt;button onclick="searchPeriods()"&gt;Search Periods&lt;/button&gt;
Period: &lt;select id="period"&gt;&lt;option value="volvo"&gt;N/A&lt;/option&gt;&lt;/select&gt;
&lt;button onclick="queryFinancials()"&gt;Get Financials&lt;/button&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;div id="grid"&gt;&lt;/div&gt;
&lt;script src="https://unpkg.com/js-spread-grid@latest/dist/index.js"&gt;&lt;/script&gt;
&lt;script src="https://cdn.plot.ly/plotly-3.1.0.min.js" charset="utf-8"&gt;&lt;/script&gt;
&lt;h2 id="graph-financials-over-time"&gt;Graph financials over time&lt;/h2&gt;
&lt;p&gt;Example queries:&lt;/p&gt;
&lt;ul&gt;
 &lt;li&gt;&lt;a href="javascript:void(0);" onclick="document.getElementById('ticker').value = 'NVDA'; document.getElementById('query').value = `( Revenues
 - CostOfRevenue
 - SellingGeneralAndAdministrativeExpense
 - ResearchAndDevelopmentExpense
 + COALESCE(NonoperatingIncomeExpense, OtherNonoperatingIncomeExpense)
 - IncomeTaxExpenseBenefit
) / Revenues`; execute()"&gt;What is NVIDIA's net profit margin?&lt;/a&gt;&lt;/li&gt;
 &lt;li&gt;&lt;a href="javascript:void(0);" onclick="document.getElementById('ticker').value = 'COST'; document.getElementById('query').value = `( COALESCE(RevenueFromContractWithCustomerExcludingAssessedTax, Revenues)
 - CostOfGoodsAndServicesSold
 - SellingGeneralAndAdministrativeExpense
 - IncomeTaxExpenseBenefit
 + InterestAndOtherIncome
 - InterestExpense
) / COALESCE(RevenueFromContractWithCustomerExcludingAssessedTax, Revenues)`; execute()"&gt;What is Costco's net profit margin?&lt;/a&gt;&lt;/li&gt;
 &lt;li&gt;&lt;a href="javascript:void(0);" onclick="document.getElementById('ticker').value = 'INTC'; document.getElementById('query').value = 'NetIncomeLoss'; execute()"&gt;When was Intel's fall from grace?&lt;/a&gt;&lt;/li&gt;
 &lt;li&gt;&lt;a href="javascript:void(0);" onclick="document.getElementById('ticker').value = 'MSTR'; document.getElementById('query').value = 'PaymentsToAcquireIntangibleAssets'; execute()"&gt;How much is Strategy spending to acquire Bitcoin?&lt;/a&gt;&lt;/li&gt;
 &lt;li&gt;&lt;a href="javascript:void(0);" onclick="document.getElementById('ticker').value = 'AAPL'; document.getElementById('query').value = 'WeightedAverageNumberOfSharesOutstandingBasic'; execute()"&gt;When has Apple performed stock splits and stock buybacks?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;textarea id="query" rows="8" style="width: 100%; font-family: 'Space mono';" onkeypress="handle(event)"&gt;&lt;/textarea&gt;
&lt;p&gt;&lt;button onclick="execute()"&gt;Run&lt;/button&gt; (Ctrl/Cmd+Enter)&lt;/p&gt;</description></item><item><title>In-browser SQL using DuckDB</title><link>/demos/in-browser-sql/</link><pubDate>Thu, 01 Jan 0009 00:00:00 +0000</pubDate><guid>/demos/in-browser-sql/</guid><description>&lt;p&gt;All queries are executed in the browser using &lt;a href="https://duckdb.org/docs/api/wasm/overview.html"&gt;DuckDB WASM&lt;/a&gt;. &lt;em&gt;P.S. I mainly use this tool when I&amp;rsquo;m too lazy to set up a proper database to test SQL queries&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Feel free to come up with your own SQL queries, as everything is running at your own expense!&lt;/p&gt;
&lt;p&gt;Example queries:&lt;/p&gt;
&lt;ul&gt;
 &lt;li&gt;&lt;a href="javascript:void(0);" onclick="document.getElementById('query').value = 'SELECT *\nFROM read_json(\'https://moneymarket.fun/data/fundYields.json\')\nWHERE category = \'Government\'\nORDER BY yield DESC'; execute()"&gt;Which US government money markets currently have the highest yield?&lt;/a&gt;&lt;/li&gt;
 &lt;li&gt;&lt;a href="javascript:void(0);" onclick="document.getElementById('query').value = 'SELECT\n &amp;quot;Primary Fur Color&amp;quot; as fur_color,\n COUNT() as occurrences,\n 100 * COUNT() / SUM(COUNT()) OVER() as percentage\nFROM \'https://data.cityofnewyork.us/api/views/vfnx-vebw/rows.csv\'\nWHERE &amp;quot;Primary Fur Color&amp;quot; IS NOT NULL\nGROUP BY &amp;quot;Primary Fur Color&amp;quot;'; execute()"&gt;What is the fur color breakdown for the 2018 NYC squirrel census?&lt;/a&gt;&lt;/li&gt;
 &lt;li&gt;&lt;a href="javascript:void(0);" onclick="document.getElementById('query').value = 'SELECT *\nFROM read_csv(\'https://data.gov.au/data/dataset/553b3049-2b8b-46a2-95e6-640d7986a8c1/resource/34076296-6692-4e30-b627-67b7c4eb1027/download/toiletmapexport_241101_074429.csv\')\nWHERE BabyChange AND Town = \'Sydney\''; execute()"&gt;Need to find a public toilet with a baby changer in Sydney, Australia?&lt;/a&gt;&lt;/li&gt;
 &lt;li&gt;&lt;a href="javascript:void(0);" onclick="document.getElementById('query').value = 'SELECT * FROM (\n SELECT unnest(outputs, recursive:=True)\n FROM read_parquet(\'s3://aws-public-blockchain/v1.0/btc/transactions/date=2024-04-20/part-00000-9c311aa4-8f1b-483d-9ef1-5d643c1d4de7-c000.snappy.parquet\')\n WHERE is_coinbase AND block_number = 840000\n)\nWHERE address IS NOT NULL'; execute()"&gt;Which address mined block 840000 which marks the 4th bitcoin halving?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;textarea id="query" rows="8" style="width: 100%; font-family: 'Space mono';" onkeypress="handle(event)"&gt;&lt;/textarea&gt;
&lt;p&gt;&lt;button onclick="execute()"&gt;Run&lt;/button&gt; (Ctrl/Cmd+Enter)&lt;/p&gt;</description></item><item><title>Real-time Ethereum Rollups Info</title><link>/demos/eth-l2/</link><pubDate>Tue, 01 Jan 0008 00:00:00 +0000</pubDate><guid>/demos/eth-l2/</guid><description>&lt;p&gt;Real-time information provided by public RPC APIs.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Yellow indicates that the chain has yet to report two blocks.&lt;/li&gt;
&lt;li&gt;Red indicates that the chain has not reported blocks for a while.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Many more chains are missing, you can comment below to have them added.&lt;/p&gt;
&lt;div id="grid"&gt;&lt;/div&gt;
&lt;script src="https://unpkg.com/js-spread-grid@latest/dist/index.js"&gt;&lt;/script&gt;
&lt;script type="module"&gt;
function pushData(arr, entry) {
 arr.push(entry);
 if (arr.length &gt; 100) {
 arr.shift();
 }
}

function getAvg(arr) {
 return arr.reduce((a, b) =&gt; a + b, 0) / arr.length;
}

function getBlocktime(block_numbers, block_times) {
 if (block_times.length &lt; 2) {
 return -1;
 }
 const number_diff =
 block_numbers[block_numbers.length - 1] - block_numbers[0];
 const time_diff = block_times[block_times.length - 1] - block_times[0];
 return (time_diff / number_diff);
}

const grid = document.getElementById("grid");

const streams = {
 Ethereum: {
 rpc: "wss://eth.drpc.org",
 stack: "-",
 url: "https://ethereum.org",
 explorer: "https://etherscan.io/block/",
 },
 Base: {
 rpc: "wss://base.drpc.org",
 stack: "Optimism",
 url: "https://www.base.org",
 explorer: "https://basescan.org/block/",
 },
 "Arbitrum One": {
 rpc: "wss://arbitrum.drpc.org",
 stack: "Arbitrum",
 url: "https://arbitrum.io/rollup",
 explorer: "https://arbiscan.io/block/",
 },
 "OP Mainnet": {
 rpc: "wss://optimism.drpc.org",
 stack: "Optimism",
 url: "https://optimism.io",
 explorer: "https://optimistic.etherscan.io/block/",
 },
 "Polygon POS": {
 rpc: "wss://polygon.drpc.org",
 stack: "Polygon",
 url: "https://polygon.technology/polygon-pos",
 explorer: "https://polygonscan.com/block/",
 },
 "zkSync Era": {
 rpc: "wss://mainnet.era.zksync.io/ws",
 stack: "-",
 url: "https://zksync.io",
 explorer: "https://explorer.zksync.io/batch/",
 },
 Blast: {
 rpc: "wss://rpc.blast.io",
 stack: "Optimism",
 url: "https://blast.io",
 explorer: "https://blastscan.io/block/",
 },
 Linea: {
 rpc: "wss://linea.drpc.org",
 stack: "-",
 url: "https://linea.build",
 explorer: "https://lineascan.build/block/",
 },
 "World Chain": {
 rpc: "wss://worldchain.drpc.org",
 stack: "Optimism",
 url: "https://world.org/world-chain",
 explorer: "https://worldscan.org/block/",
 },
 Scroll: {
 rpc: "wss://scroll.drpc.org",
 stack: "-",
 url: "https://scroll.io",
 explorer: "https://scrollscan.com/block/",
 },
 Zircuit: {
 rpc: "wss://zircuit-mainnet.drpc.org",
 stack: "Optimism",
 url: "https://www.zircuit.com",
 explorer: "https://explorer.zircuit.com/blocks/",
 },
 "Build On Bitcoin": {
 rpc: "wss://bob.drpc.org",
 stack: "Optimism",
 url: "https://www.gobob.xyz",
 explorer: "https://3xpl.com/bob/block/",
 },
 Mode: {
 rpc: "wss://mainnet.mode.network",
 stack: "Optimism",
 url: "https://mode.network",
 explorer: "https://explorer.mode.network/block/",
 },
 Taiko: {
 rpc: "wss://taiko.drpc.org",
 stack: "-",
 url: "https://taiko.xyz",
 explorer: "https://taikoscan.io/block/",
 },
 Fraxtal: {
 rpc: "wss://fraxtal.drpc.org",
 stack: "Optimism",
 url: "https://frax.com",
 explorer: "https://fraxscan.com/block/",
 },
 Metis: {
 rpc: "wss://metis-mainnet.blastapi.io/f532a60e-286a-44b9-8455-e1aebc8bc21d",
 stack: "-",
 url: "https://metis.io",
 explorer: "https://explorer.metis.io/batch/",
 },
 Lisk: {
 rpc: "wss://ws.api.lisk.com",
 stack: "Optimism",
 url: "https://lisk.com",
 explorer: "https://blockscout.lisk.com/block/",
 },
 Ink: {
 rpc: "wss://ws-gel.inkonchain.com",
 stack: "Optimism",
 url: "https://inkonchain.com",
 explorer: "https://explorer.inkonchain.com/block/",
 },
 Soneium: {
 rpc: "wss://rpc.soneium.org",
 stack: "Optimism",
 url: "https://soneium.org",
 explorer: "https://soneium.blockscout.com/block/",
 },
 Unichain: {
 rpc: "wss://unichain.drpc.org",
 stack: "Optimism",
 url: "https://www.unichain.org",
 explorer: "https://uniscan.xyz/block/",
 },
 Morph: {
 rpc: "wss://rpc-quicknode.morphl2.io",
 stack: "Optimism",
 url: "https://www.morphl2.io",
 explorer: "https://explorer.morphl2.io/block/",
 },
 Zora: {
 rpc: "wss://rpc.zora.energy",
 stack: "Optimism",
 url: "https://zora.energy",
 explorer: "https://explorer.zora.energy/block/",
 },
 Mint: {
 rpc: "wss://rpc.mintchain.io",
 stack: "Optimism",
 url: "https://mintchain.io",
 explorer: "https://explorer.mintchain.io/block/",
 },
 Redstone: {
 rpc: "wss://rpc.redstonechain.com",
 stack: "Optimism",
 url: "https://redstone.xyz",
 explorer: "https://explorer.redstone.xyz/block/",
 },
 Xai: {
 rpc: "wss://xai-mainnet.rpc.quicknode.com",
 stack: "Arbitrum",
 url: "https://xai.games",
 explorer: "https://xaiscan.io/block/",
 },
 Cyber: {
 rpc: "wss://rpc.cyber.co",
 stack: "Optimism",
 url: "https://cyber.co",
 explorer: "https://cyberscan.co/block/",
 },
 Gnosis: {
 rpc: "wss://rpc.gnosischain.com/wss",
 stack: "-",
 url: "https://www.gnosischain.com/",
 explorer: "https://gnosisscan.io/block/",
 },
 ApeChain: {
 rpc: "wss://apechain.calderachain.xyz/ws",
 stack: "Arbitrum",
 url: "https://apechain.com",
 explorer: "https://apescan.io/block/",
 },
 "Immutable zkEVM": {
 rpc: "wss://stylish-hardworking-dinghy.imx-mainnet.quiknode.pro/0166596fe26132eb6c819355aad99d72299e8d79",
 stack: "-",
 url: "https://www.immutable.com/",
 explorer: "https://explorer.immutable.com/block/",
 },
 "Polygon zkEVM": {
 rpc: "wss://polygon-zkevm-mainnet.blastapi.io/f532a60e-286a-44b9-8455-e1aebc8bc21d",
 stack: "Polygon",
 url: "https://polygon.technology/polygon-zkevm",
 explorer: "https://zkevm.polygonscan.com/block/",
 },
 "Arbitrum Nova": {
 rpc: "wss://arbitrum-nova.blastapi.io/f532a60e-286a-44b9-8455-e1aebc8bc21d",
 stack: "Arbitrum",
 url: "https://arbitrum.io/anytrust",
 explorer: "https://nova.arbiscan.io/block/",
 },
};

const stacks = {
 Optimism: "https://www.optimism.io",
 Arbitrum: "https://arbitrum.io/orbit",
 Polygon: "https://polygon.technology",
};

const data = {};
const display_data = {};

display_data["Σ"] = {
 Chain: "Σ",
 Stack: "-",
 "Block Number": 0,
 TPS: -1,
 "Mgas/s": -1,
 "KB/s": -1,
 "Block Time": -1,
 Timestamp: -1,
};

for (const [name, stream] of Object.entries(streams)) {
 data[name] = {
 block_numbers: [],
 transactions: [],
 gas: [],
 data: [],
 block_times: [],
 };
 display_data[name] = {
 Chain: name,
 Stack: stream.stack,
 "Block Number": -1,
 TPS: -1,
 "Mgas/s": -1,
 "KB/s": -1,
 "Block Time": -1,
 Timestamp: -1,
 };
}

for (const [name, stream] of Object.entries(streams)) {
 const ws = new WebSocket(stream.rpc);
 ws.onopen = () =&gt; {
 ws.send(
 JSON.stringify({
 jsonrpc: "2.0",
 id: 1,
 method: "eth_subscribe",
 params: ["newHeads"],
 }),
 );
 };

 ws.onmessage = (event) =&gt; {
 const message = JSON.parse(event.data);
 if (message.method !== "eth_subscription") {
 if (
 message.id === 2 &amp;&amp;
 "result" in message &amp;&amp;
 "transactions" in message.result &amp;&amp;
 "size" in message.result
 ) {
 pushData(data[name].transactions, message.result.transactions.length);
 pushData(data[name].data, parseInt(message.result.size, 16));
 }
 return;
 }

 const result = message.params.result;
 setTimeout(() =&gt; {
 ws.send(
 JSON.stringify({
 jsonrpc: "2.0",
 id: 2,
 method: "eth_getBlockByNumber",
 params: [result.number, false],
 }),
 );
 }, 1000);

 const block_number = parseInt(result.number, 16);
 pushData(data[name].block_numbers, block_number);

 const timestamp = parseInt(result.timestamp, 16);
 pushData(data[name].block_times, timestamp);

 pushData(data[name].gas, parseInt(result.gasUsed, 16));

 display_data[name]["Block Number"] = block_number;
 display_data[name]["Timestamp"] = timestamp;

 const block_time = getBlocktime(
 data[name].block_numbers,
 data[name].block_times,
 );
 if (block_time &gt; 0) {
 if (data[name].transactions.length !== 0) {
 display_data[name]["TPS"] =
 getAvg(data[name].transactions) / block_time;
 }
 if (data[name].gas.length !== 0) {
 display_data[name]["Mgas/s"] =
 getAvg(data[name].gas) / 1e6 / block_time;
 }
 if (data[name].data.length !== 0) {
 display_data[name]["KB/s"] = getAvg(data[name].data) / 1024;
 }
 display_data[name]["Block Time"] = block_time;
 }

 let sigma_tps = 0;
 let sigma_mgas = 0;
 let sigma_kbs = 0;
 let sigma_block_time = 0;
 for (const key in display_data) {
 const item = display_data[key];
 if (item["Chain"] === "Σ") {
 continue;
 }
 if (item["TPS"] !== -1) {
 sigma_tps += item["TPS"];
 }
 if (item["Mgas/s"] !== -1) {
 sigma_mgas += item["Mgas/s"];
 }
 if (item["KB/s"] !== -1) {
 sigma_kbs += item["KB/s"];
 }
 if (item["Block Time"] !== -1) {
 sigma_block_time += 1 / item["Block Time"];
 }
 }

 display_data["Σ"]["Block Number"]++;
 display_data["Σ"]["TPS"] = sigma_tps;
 display_data["Σ"]["Mgas/s"] = sigma_mgas;
 display_data["Σ"]["KB/s"] = sigma_kbs;
 display_data["Σ"]["Block Time"] = 1 / sigma_block_time;
 display_data["Σ"]["Timestamp"] = Date.now() / 1000;

 const formatting = [
 {
 rows: {},
 style: ({ row }) =&gt; {
 if (row.id === "Σ") {
 return {};
 }
 const block_time = getBlocktime(
 data[row.id].block_numbers,
 data[row.id].block_times,
 );
 if (block_time === -1) {
 return {};
 }
 const current_time = Math.round(Date.now() / 1000);
 const last_block_time =
 data[row.id].block_times[data[row.id].block_times.length - 1];
 if (current_time - last_block_time &gt; block_time + 20) {
 return { background: "#f6d6d6" };
 }
 },
 },
 {
 rows: {},
 style: ({ row }) =&gt; {
 if (display_data[row.id]["TPS"] !== -1) {
 return {};
 }
 return { background: "#f6f6d6" };
 },
 },
 {
 font: "16px Space Mono",
 },
 {
 column: { id: "Block Number" },
 style: { textAlign: "right" },
 text: ({ value }) =&gt; (value === -1 ? "-" : value),
 },
 {
 column: { id: "TPS" },
 style: { textAlign: "right" },
 text: ({ value }) =&gt; (value === -1 ? "-" : value.toFixed(2)),
 },
 {
 column: { id: "Block Time" },
 style: { textAlign: "right" },
 text: ({ value }) =&gt; (value === -1 ? "-" : value.toFixed(2)),
 },
 {
 column: { id: "Block Time" },
 draw: ({ ctx, value, column, row }) =&gt; {
 if ((value === -1) || (row.id === "Σ")) {
 return;
 }
 const lag = (
 display_data["Σ"]["Timestamp"] - display_data[row.id]["Timestamp"]
 );
 const lag_percent = Math.max(0, Math.min(1, lag / value));
 const width = column.width - 4;
 const height = row.height - 4;
 const barWidth = width * lag_percent;
 ctx.fillStyle = '#d6e6f6';
 ctx.fillRect(2, 2, barWidth, height);
 }
 },
 {
 column: { id: "Timestamp" },
 style: { textAlign: "right" },
 text: ({ value }) =&gt; (value === -1 ? "-" : value.toFixed(0)),
 },
 {
 column: { id: "Mgas/s" },
 style: { textAlign: "right" },
 text: ({ value }) =&gt; (value === -1 ? "-" : value.toFixed(2)),
 },
 {
 column: { id: "KB/s" },
 style: { textAlign: "right" },
 text: ({ value }) =&gt; (value === -1 ? "-" : value.toFixed(2)),
 },
 ];

 SpreadGrid(grid, {
 data: display_data,
 columns: [
 {
 type: "DATA-BLOCK",
 width: "fit",
 },
 ],
 rows: [
 {
 type: "HEADER",
 height: 20,
 },
 {
 type: "DATA-BLOCK",
 height: 20,
 },
 ],
 formatting: formatting,
 onCellClick: ({ columnId, rowId }) =&gt; {
 if (columnId === "Chain" &amp;&amp; rowId in streams) {
 window.open(streams[rowId].url, "_blank");
 }
 if (columnId === "Stack" &amp;&amp; display_data[rowId]["Stack"] in stacks) {
 window.open(stacks[display_data[rowId]["Stack"]], "_blank");
 }
 if (
 columnId === "Block Number" &amp;&amp;
 rowId in streams &amp;&amp;
 display_data[rowId]["Block Number"] !== -1
 ) {
 window.open(
 streams[rowId].explorer + display_data[rowId]["Block Number"],
 "_blank",
 );
 }
 },
 });
 };
}
&lt;/script&gt;</description></item><item><title>Real-time Prices with Pyth Network</title><link>/demos/pyth-network/</link><pubDate>Mon, 01 Jan 0007 00:00:00 +0000</pubDate><guid>/demos/pyth-network/</guid><description>&lt;p&gt;Real-time prices for the &lt;a href="https://www.marketvector.com/factsheets/download/COIN50.d.pdf"&gt;MarketVector Coinbase 50 Index (COIN50)&lt;/a&gt; provided by the &lt;a href="https://pythdata.app/explore"&gt;Pyth Network&lt;/a&gt;.&lt;/p&gt;
&lt;div id="grid"&gt;&lt;/div&gt;
&lt;script src="https://unpkg.com/js-spread-grid@latest/dist/index.js"&gt;&lt;/script&gt;
&lt;script type="module"&gt;
const grid = document.getElementById("grid");

const streams = {
 e62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43: {
 name: "Bitcoin",
 symbol: "BTC",
 },
 ff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace: {
 name: "Ethereum",
 symbol: "ETH",
 },
 ec5d399846a9209f3fe5881d70aae9268c94339ff9817e8d18ff19fa05eea1c8: {
 name: "XRP",
 symbol: "XRP",
 },
 ef0d8b6fda2ceba41da15d4095d1da392a0d2f8ed0c6c7bc0f4cfac8c280b56d: {
 name: "Solana",
 symbol: "SOL",
 },
 dcef50dd0a4cd2dcc17e45df1676dcb336a11a61c69df7a0299b0150c672d25c: {
 name: "Dogecoin",
 symbol: "DOGE",
 },
 "2a01deaec9e51a579277b34b122399984d0bbf57e2458a7e42fecd2829867a0d": {
 name: "Cardano",
 symbol: "ADA",
 },
 "3dd2b63686a450ec7290df3a1e0b583c0481f651351edfa7636f39aed55cf8a3": {
 name: "Bitcoin Cash",
 symbol: "BCH",
 },
 "8ac0c70fff57e9aefdf5edf44b51d62c2d433653cbb2cf5cc06bb115af04d221": {
 name: "Chainlink",
 symbol: "LINK",
 },
 b7a8eba68a997cd0210c2e1e4ee811ad2d174b3611c22d9ebf16f4cb7e9ba850: {
 name: "Stellar",
 symbol: "XLM",
 },
 "6e3f3fa8253588df9326580180233eb791e03b443a3ba7a1d892e73874e19a54": {
 name: "Litecoin",
 symbol: "LTC",
 },
 "93da3352f9f1d105fdfe4971cfa80e9dd777bfc5d0f683ebb6e1294b92137bb7": {
 name: "Avalanche",
 symbol: "AVAX",
 },
 "3728e591097635310e6341af53db8b7ee42da9b3a8d918f9463ce9cca886dfbd": {
 name: "Hedera",
 symbol: "HBAR",
 },
 f0d57deca57b3da2fe63a493f4c25925fdfd8edf834b20f93e1f84dbd1504d4a: {
 name: "Shiba Inu",
 symbol: "SHIB",
 },
 "410f41de235f2db824e562ea7ab2d3d3d4ff048316c61d629c0b93f58584e1af": {
 name: "Bittensor",
 symbol: "TAO",
 },
 "78d185a741d07edb3412b09008b7c5cfb9bbbd7d568bf00ba737b456ba171501": {
 name: "Uniswap",
 symbol: "UNI",
 },
 ca3eed9b267293f6595901c734c7525ce8ef49adafe8284606ceb307afa2ca5b: {
 name: "Polkadot",
 symbol: "DOT",
 },
 "4e3037c822d852d79af3ac80e35eb420ee3b870dca49f9344a38ef4773fb0585": {
 name: "Mantle",
 symbol: "MNT",
 },
 c415de8d2eba7db216527dff4b60e8f3a5311c740dadb233e13e12547e226750: {
 name: "Near",
 symbol: "NEAR",
 },
 d69731a2e74ac1ce884fc3890f7ee324b6deb66147055249568869ed700882e4: {
 name: "Pepe",
 symbol: "PEPE",
 },
 "a483243eed64ca27a1f6e26385b7d1e0d07e9fe264bb6903efb3efc4689d3fe7": {
 name: "Sky",
 symbol: "SKY",
 },
 c9907d786c5821547777780a1e4f89484f3417cb14dd244f2b0a34ea7a554d67: {
 name: "Internet Computer",
 symbol: "ICP",
 },
 "2b9ab1e972a281585084148ba1389800799bd4be63b957507db1349314e47445": {
 name: "Aave",
 symbol: "AAVE",
 },
 "7f5cc8d963fc5b3d2ae41fe5685ada89fd4f14b435f8050f28c7fd409f40c2d8": {
 name: "Ethereum Classic",
 symbol: "ETC",
 },
 fa17ceaf30d19ba51112fdcc750cc83454776f47fb0112e4af07f15f4bb1ebc0: {
 name: "Algorand",
 symbol: "ALGO",
 },
 b00b60f88b03a6a625a8d1c048c3f66653edf217439983d037e7222c4e612819: {
 name: "Cosmos",
 symbol: "ATOM",
 },
 ffd11c5a1cfd42f80afb2df4d9f264c15f956d68153335374ec10722edd70472: {
 name: "Polygon",
 symbol: "POL",
 },
 "b7910ba7322db020416fcac28b48c01212fd9cc8fbcbaf7d30477ed8605f6bd4": {
 name: "Ethena",
 symbol: "ENA",
 },
 "3d4a2bd9535be6ce8059d75eadeba507b043257321aa544717c56fa19b49e35d": {
 name: "Render",
 symbol: "RENDER",
 },
 "19ab139032007c8bd7d1fd3842ef392a5434569a72b555504a5aee47df2a0a35": {
 name: "Quant",
 symbol: "QNT",
 },
 "03ae4db29ed4ae33d323568895aa00337e658e348b37509f5372ae51f0af00d5": {
 name: "Aptos",
 symbol: "APT",
 },
 "3fa4252848f9f0a1480be62745a4629d9eb1322aebab8a791e344b3b9c1adcf5": {
 name: "Arbitrum",
 symbol: "ARB",
 },
 "035aa8d0a2d74e19438f2c1440edff9f3b95f915ca65f681a25ed0bad3dc228d": {
 name: "Flare",
 symbol: "FLR",
 },
 "1722176f738aa1aafea170f8b27724042c5ac6d8cb9cf8ae02d692b0927e0681": {
 name: "VeChain",
 symbol: "VET",
 },
 "72b021217ca3fe68922a19aaf990109cb9d84e9ad004b4d2025ad6f529314419": {
 name: "Bonk",
 symbol: "BONK",
 },
 "7a5bc1d2b56ad029048cd63964b3ad2776eadf812edc1a43a31406cb54bff592": {
 name: "Injective",
 symbol: "INJ",
 },
 ec7a775f46379b5e943c3526b1c8d54cd49749176b0b98e02dde68d1bd335c17: {
 name: "Stacks",
 symbol: "STX",
 },
 "7da003ada32eabbac855af3d22fcf0fe692cc589f0cfd5ced63cf0bdcc742efe": {
 name: "Artificial Superintelligence Alliance",
 symbol: "FET",
 },
 "53614f1cb0c031d4af66c04cb9c756234adad0e1cee85303795091499a4084eb": {
 name: "Sei",
 symbol: "SEI",
 },
 e799f456b358a2534aa1b45141d454ac04b444ed23b1440b778549bb758f2b5c: {
 name: "Chiliz",
 symbol: "CHZ",
 },
 "09f7c1d7dfbb7df2b8fe3d3d87ee94a2259d212da4f30c1f0540d066dfa44723": {
 name: "Celestia",
 symbol: "TIA",
 },
 "941320a8989414874de5aa2fc340a75d5ed91fdff1613dd55f83844d52ea63a2": {
 name: "Immutable X",
 symbol: "IMX",
 },
 "0affd4b8ad136a21d79bc82450a325ee12ff55a235abc242666e423b8bcffd03": {
 name: "Tezos",
 symbol: "XTZ",
 },
 a19d04ac696c7a6616d291c7e5d1377cc8be437c327b75adb5dc1bad745fcae8: {
 name: "Curve DAO",
 symbol: "CRV",
 },
 "b27578a9654246cb0a2950842b92330e9ace141c52b63829cc72d5c45a5a595a": {
 name: "Ether.fi",
 symbol: "ETHFI",
 },
 c63e2a7f37a04e5e614c07238bedb25dcc38927fba8fe890597a593c0b2fa4ad: {
 name: "Lido DAO",
 symbol: "LDO",
 },
 "2b386bdca7fda5cf3c3975f70318593bf144104cb00742592ecff60dd798972f": {
 name: "JasmyCoin",
 symbol: "JASMY",
 },
 "4d1f8dae0d96236fb98e8f47471a366ec3b1732b47041781934ca3a9bb2f35e7": {
 name: "The Graph",
 symbol: "GRT",
 },
 cb7a1d45139117f8d3da0a4b67264579aa905e3b124efede272634f094e1e9d1: {
 name: "The Sandbox",
 symbol: "SAND",
 },
 "1dfffdcbc958d732750f53ff7f06d24bb01364b3f62abea511a390c74b8d16a5": {
 name: "Decentraland",
 symbol: "MANA",
 },
 "649fdd7ec08e8e2a20f425729854e90293dcbe2376abc47197a14da6ff339756": {
 name: "Helium",
 symbol: "HNT",
 },
};

const data = {};

for (const [id, obj] of Object.entries(streams)) {
 data[id] = {
 Name: obj.name,
 Symbol: obj.symbol,
 "Price USD": 0,
 };
}

const ws = new WebSocket("wss://hermes.pyth.network/ws");
ws.onopen = () =&gt; {
 ws.send(
 JSON.stringify({
 ids: Object.keys(streams),
 type: "subscribe",
 })
 );
};

ws.onmessage = (event) =&gt; {
 const message = JSON.parse(event.data);
 if (message.type !== "price_update") {
 return;
 }

 const id = message.price_feed.id;
 data[id]["Price USD"] =
 parseInt(message.price_feed.price.price) *
 Math.pow(10, message.price_feed.price.expo);

 SpreadGrid(grid, {
 data: data,
 columns: [
 {
 type: "DATA-BLOCK",
 width: 200,
 },
 ],
 });
};
&lt;/script&gt;</description></item><item><title>FFmpeg Wasm YouTube Downloader</title><link>/demos/ffmpeg-wasm-youtube-downloader/</link><pubDate>Sun, 01 Jan 0006 00:00:00 +0000</pubDate><guid>/demos/ffmpeg-wasm-youtube-downloader/</guid><description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: The demo currently doesn&amp;rsquo;t work due to recent changes to YouTube&amp;rsquo;s
API. If you have any ideas on how to fix it in a browser setting, please open
an issue on &lt;a href="https://github.com/darenliang/darenliang.com"&gt;GitHub&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;This is a simple browser-based app (using &lt;a href="https://github.com/ffmpegwasm/ffmpeg.wasm"&gt;ffmpeg.wasm&lt;/a&gt;
and &lt;a href="https://github.com/fent/node-ytdl-core"&gt;node-ytdl-core&lt;/a&gt;) that allows you to download YouTube videos without the
need to use &lt;a href="https://youtube-dl.org/"&gt;youtube-dl&lt;/a&gt; locally.&lt;/p&gt;
&lt;p&gt;There are four options in this demo:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Get formats&lt;/strong&gt;: Manually select formats to merge. If video is selected, the output will be a video. If only audio is selected, the output will be an audio file.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Get best video&lt;/strong&gt;: Encode the highest quality video file with audio. The best video and audio files are merged into a single file.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Get best audio&lt;/strong&gt;: Encode the highest quality audio file.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The source code can be found &lt;a href="https://github.com/darenliang/darenliang.com/tree/master/misc/ytdl-ffmpeg-demo"&gt;here&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Imglabs.io Image Proxy Demo</title><link>/demos/imglabs/</link><pubDate>Sat, 01 Jan 0005 00:00:00 +0000</pubDate><guid>/demos/imglabs/</guid><description>&lt;p&gt;Imglabs.io is an image proxy that caches and transforms images on the network
edge. The image proxy core is written from the ground up in Rust and compiled
to WebAssembly and deployed to hundreds of edge locations around the world
to achieve industry-leading image proxying latency and performance.&lt;/p&gt;
&lt;p&gt;Want to give a try on your website or project? You can sign up and proxy images for free
at &lt;a href="https://www.imglabs.io/signup"&gt;Imglabs.io&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;label&gt;Image URL: &lt;input type="url" id="url" style="width: 100%" placeholder="https://www.darenliang.com/img/imglabs/example.jpg"&gt;&lt;/label&gt;
&lt;br&gt;&lt;button onclick="getImage()"&gt;Get Image&lt;/button&gt;&lt;/p&gt;</description></item><item><title>This Is Your Weather Forecast</title><link>/demos/weather/</link><pubDate>Thu, 01 Jan 0004 00:00:00 +0000</pubDate><guid>/demos/weather/</guid><description>&lt;style&gt;
.weather-table {
 table-layout: fixed;
 width: 100%;
}
details {
 padding-top: 1em;
}
&lt;/style&gt;
&lt;div id="weather"&gt;&lt;/div&gt;
&lt;p&gt;Powered by &lt;a href="https://weatherstack.com/"&gt;weatherstack&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>3D Shapes Using the 2D Canvas API</title><link>/demos/3d-canvas/</link><pubDate>Wed, 01 Jan 0003 00:00:00 +0000</pubDate><guid>/demos/3d-canvas/</guid><description>&lt;p&gt;This is a JavaScript reimplementation
of &lt;a href="https://github.com/darenliang/pygame3Dtest"&gt;pygame3Dtest&lt;/a&gt;,
which is a simple pygame demo I wrote to show how to draw 3D shapes using the
pygame API.&lt;/p&gt;
&lt;p&gt;Press the arrow keys ⬆️ ⬇️ ⬅️ ➡️ or drag on mobile to rotate the shape!&lt;/p&gt;
&lt;div class="controls"&gt;
 &lt;button id="scrollToggleBtn"&gt;Disable Arrow Key Scroll&lt;/button&gt;
 &lt;button id="cubeBtn"&gt;Cube&lt;/button&gt;
 &lt;button id="tetraBtn"&gt;Tetrahedron&lt;/button&gt;
 &lt;button id="octaBtn"&gt;Octahedron&lt;/button&gt;
 &lt;button id="icosaBtn"&gt;Icosahedron&lt;/button&gt;
&lt;/div&gt;
&lt;p&gt;&lt;canvas id="canvas3D"&gt;&lt;/canvas&gt;&lt;/p&gt;
&lt;script&gt;
 const article = document.querySelector('article');

 const shapes = {
 cube: {
 points: [
 [-2, -2, -2],
 [ 2, -2, -2],
 [ 2, 2, -2],
 [-2, 2, -2],
 [-2, -2, 2],
 [ 2, -2, 2],
 [ 2, 2, 2],
 [-2, 2, 2]
 ],
 edges: [
 [0,1], [1,2], [2,3], [3,0],
 [4,5], [5,6], [6,7], [7,4],
 [0,4], [1,5], [2,6], [3,7]
 ]
 },
 tetrahedron: {
 points: [
 [ 2, 2, 2],
 [ 2, -2, -2],
 [-2, 2, -2],
 [-2, -2, 2]
 ],
 edges: [
 [0,1], [1,2], [2,0],
 [0,3], [1,3], [2,3]
 ]
 },
 octahedron: {
 points: [
 [ 3, 0, 0],
 [-3, 0, 0],
 [ 0, 3, 0],
 [ 0, -3, 0],
 [ 0, 0, 3],
 [ 0, 0, -3]
 ],
 edges: [
 [0,2], [0,3], [0,4], [0,5],
 [1,2], [1,3], [1,4], [1,5],
 [2,4], [2,5], [3,4], [3,5]
 ]
 },
 icosahedron: {
 points: (function() {
 const t = (1 + Math.sqrt(5)) * 1.5 / 2;
 return [
 [-1.5, t, 0], [ 1.5, t, 0], [-1.5, -t, 0], [ 1.5, -t, 0],
 [ 0, -1.5, t], [ 0, 1.5, t], [ 0, -1.5, -t], [ 0, 1.5, -t],
 [ t, 0, -1.5], [ t, 0, 1.5], [-t, 0, -1.5], [-t, 0, 1.5]
 ];
 })(),
 edges: [
 [0,1], [0,5], [0,7], [0,10], [0,11],
 [1,5], [1,7], [1,8], [1,9],
 [2,3], [2,4], [2,6], [2,10], [2,11],
 [3,4], [3,6], [3,8], [3,9],
 [4,5], [4,9], [4,11],
 [5,9], [5,11],
 [6,7], [6,8], [6,10],
 [7,8], [7,10],
 [8,9],
 [10,11]
 ]
 }
 };

 const canvas = document.getElementById('canvas3D');
 const ctx = canvas.getContext('2d');

 let currentShape = shapes.cube;

 let angleX;
 let angleY;

 function resetAngles() {
 angleX = -Math.PI / 4;
 angleY = -Math.PI / 4;
 }
 resetAngles();

 const rotationSpeed = 1;

 const keysPressed = {
 ArrowUp: false,
 ArrowDown: false,
 ArrowLeft: false,
 ArrowRight: false
 };

 const focalLength = 200;
 let scale = article.clientWidth / 8;

 let lastTimestamp = 0;

 let arrowScrollDisabled = false;

 document.addEventListener('keydown', (e) =&gt; {
 if (e.key in keysPressed) {
 keysPressed[e.key] = true;
 }
 });
 document.addEventListener('keyup', (e) =&gt; {
 if (e.key in keysPressed) {
 keysPressed[e.key] = false;
 }
 });

 const darkThemeMq = window.matchMedia("(prefers-color-scheme: dark)");
 let lineColor = darkThemeMq.matches ? "#CCCCCC" : "#0D1117";
 darkThemeMq.addEventListener("change", (e) =&gt; {
 lineColor = e.matches ? "#CCCCCC" : "#0D1117";
 });

 function resizeCanvas() {
 canvas.width = article.clientWidth;
 canvas.height = article.clientWidth;
 scale = article.clientWidth / 8;
 draw(lastTimestamp);
 }

 resizeCanvas();

 window.addEventListener('resize', resizeCanvas);

 function rotateX([x, y, z], radians) {
 const cos = Math.cos(radians);
 const sin = Math.sin(radians);
 return [x, y * cos - z * sin, y * sin + z * cos];
 }

 function rotateY([x, y, z], radians) {
 const cos = Math.cos(radians);
 const sin = Math.sin(radians);
 return [x * cos + z * sin, y, -x * sin + z * cos];
 }

 function project([x, y, z]) {
 const factor = focalLength / (focalLength + z);
 const X2D = x * factor * scale + canvas.width / 2;
 const Y2D = -y * factor * scale + canvas.height / 2;
 return [X2D, Y2D];
 }

 function draw(timestamp) {
 const dt = (timestamp - lastTimestamp) / 1000;
 lastTimestamp = timestamp;

 if (keysPressed.ArrowUp) {
 angleX -= rotationSpeed * dt;
 }
 if (keysPressed.ArrowDown) {
 angleX += rotationSpeed * dt;
 }
 if (keysPressed.ArrowLeft) {
 angleY -= rotationSpeed * dt;
 }
 if (keysPressed.ArrowRight) {
 angleY += rotationSpeed * dt;
 }

 ctx.clearRect(0, 0, canvas.width, canvas.height);

 const transformedPoints = currentShape.points.map((p) =&gt; {
 const rx = rotateX(p, angleX);
 const ry = rotateY(rx, angleY);
 return project(ry);
 });

 ctx.strokeStyle = lineColor;
 ctx.lineWidth = 1.5;
 ctx.beginPath();
 currentShape.edges.forEach(([start, end]) =&gt; {
 const [x1, y1] = transformedPoints[start];
 const [x2, y2] = transformedPoints[end];
 ctx.moveTo(x1, y1);
 ctx.lineTo(x2, y2);
 });
 ctx.stroke();

 requestAnimationFrame(draw);
 }

 requestAnimationFrame(draw);

 const cubeBtn = document.getElementById('cubeBtn');
 const tetraBtn = document.getElementById('tetraBtn');
 const octaBtn = document.getElementById('octaBtn');
 const icosaBtn = document.getElementById('icosaBtn');
 const scrollToggleBtn = document.getElementById('scrollToggleBtn');

 cubeBtn.addEventListener('click', () =&gt; {
 currentShape = shapes.cube;
 resetAngles();
 });
 tetraBtn.addEventListener('click', () =&gt; {
 currentShape = shapes.tetrahedron;
 resetAngles();
 });
 octaBtn.addEventListener('click', () =&gt; {
 currentShape = shapes.octahedron;
 resetAngles();
 });
 icosaBtn.addEventListener('click', () =&gt; {
 currentShape = shapes.icosahedron;
 resetAngles();
 });

 scrollToggleBtn.addEventListener('click', () =&gt; {
 arrowScrollDisabled = !arrowScrollDisabled;
 scrollToggleBtn.textContent = arrowScrollDisabled
 ? 'Enable Arrow Key Scroll'
 : 'Disable Arrow Key Scroll';
 });

 window.addEventListener('keydown', (e) =&gt; {
 if (arrowScrollDisabled &amp;&amp; (
 e.key === 'ArrowUp' ||
 e.key === 'ArrowDown' ||
 e.key === 'ArrowLeft' ||
 e.key === 'ArrowRight'
 )) {
 e.preventDefault();
 }
 }, { passive: false });

 let touchStartX = 0;
 let touchStartY = 0;

 canvas.addEventListener('touchstart', (e) =&gt; {
 const touch = e.touches[0];
 touchStartX = touch.clientX;
 touchStartY = touch.clientY;
 e.preventDefault();
 });

 canvas.addEventListener('touchmove', (e) =&gt; {
 const touch = e.touches[0];
 const touchEndX = touch.clientX;
 const touchEndY = touch.clientY;

 const deltaX = touchEndX - touchStartX;
 const deltaY = touchEndY - touchStartY;

 angleY += deltaX * 0.01;
 angleX += deltaY * 0.01;

 touchStartX = touchEndX;
 touchStartY = touchEndY;
 e.preventDefault();
 });
&lt;/script&gt;</description></item><item><title>CS:GO Crosshair Preview</title><link>/demos/csgo-crosshair-preview/</link><pubDate>Tue, 01 Jan 0002 00:00:00 +0000</pubDate><guid>/demos/csgo-crosshair-preview/</guid><description>&lt;p&gt;This is a simple demo which utilizes &lt;a href="https://workers.cloudflare.com/"&gt;Cloudflare Workers&lt;/a&gt; to generate CS:GO crosshair
images.&lt;/p&gt;
&lt;p&gt;To learn more about how this demo: &lt;a href="/posts/generating-images-with-serverless/"&gt;Generating Images with Serverless&lt;/a&gt;&lt;/p&gt;
&lt;style&gt;
@media (prefers-color-scheme: light) {
 img {
 background-color: black;
 }
}
&lt;/style&gt;
&lt;div style="display: flex; gap: 10px;"&gt;
&lt;div style="width: 80px; text-align: center;"&gt;&lt;img width="64px" height="64px" id="image"&gt;&lt;button id="copy" onclick="copyLink()"&gt;Copy&lt;/button&gt;&lt;/div&gt;
&lt;textarea id="input" style="height: 150px; width: 99%; resize: vertical; flex-grow: 1; font-size: 14px;" placeholder="Enter your crosshair commands here&amp;#10;&amp;#10;Example: cl_crosshairsize 2.5; cl_crosshairgap -3; cl_crosshairdot 0; cl_crosshair_drawoutline 0; cl_crosshaircolor 1; cl_crosshairthickness 1.5; " oninput="updatePreview()"&gt;&lt;/textarea&gt;
&lt;/div&gt;
&lt;p id="latency"&gt;Loaded image in: - ms&lt;/p&gt;
&lt;p&gt;Note: you can sometimes get super low image loading values, this is caused by fetching the image previously saved in
cache.&lt;/p&gt;</description></item><item><title>About me</title><link>/about/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>/about/</guid><description>&lt;p&gt;Hi there!&lt;/p&gt;
&lt;p&gt;I am Daren Liang, a graduate of the &lt;a href="https://www.utoronto.ca/about-u-of-t"&gt;University of Toronto&lt;/a&gt; and currently a software engineer in risk management at &lt;a href="https://www.citigroup.com"&gt;Citi&lt;/a&gt; based in NYC.&lt;/p&gt;
&lt;p&gt;In my free time, I work on &lt;a href="https://www.imglabs.io/"&gt;Imglabs.io&lt;/a&gt;, a cloud-based image proxy service, and other &lt;a href="/projects"&gt;programming projects&lt;/a&gt;. You can find me on
GitHub &lt;a href="https://github.com/darenliang" title="darenliang's GitHub"&gt;@darenliang&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Additionally, I like exploring the beauty of mathematics and probability. Feel free to email me any interesting problems
at &lt;a href="mailto:daren@darenliang.com"&gt;daren@darenliang.com&lt;/a&gt;. I&amp;rsquo;ll take a shot at them!&lt;/p&gt;</description></item><item><title>Here is my public key</title><link>/pgp/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>/pgp/</guid><description>&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-----BEGIN PGP PUBLIC KEY BLOCK-----
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mQINBGFu19IBEADWNvGzty/wDXXY7QfZacInW/ufe+8a61lZu84HF8uymkPr+SIR
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;7WP95ALMxV1VEhoII9JOb1n+i0l67pjHdZDUfRoO9MQLU1QYC55Sx4AmIoiMJBaF
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;N9NXk1b1NyD8CqQI2xwIl3UOdS0nTBtRMH3hNXukl9W/IWpM0jmZpybDm7H8Qm8y
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;PrEleLe5AYIx2GXElFuHX82P+3le6hbE28BSTSkLT436ESZWQZBh+LpAhzChXBtU
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;5ZK/mjdt/xGFeY8YdO+w3dynLhFIjuT25SsQvkcv5fOtJ8hF+B6Hbj6wOFGr+WCq
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;k12NB6Sl+rymHaZkxltZ2HK6ly+EJKswY28YGT82OsfhDr07Pd83mFpKbIP7bvH/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;k/3Wtck4zV3+kYpt4JHDhppWdsrrkgdo40OeJjAHbNi2xZ+rGfT/521Nu+t23xUG
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kf7yGQ+jTLOeRXqNbj5cP1ViNdEk8KQi0pWuEw3IM42PrMetfUCaBX/aHLHel3aZ
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;7xEOpphiIQI8nxUnEiz1f0g2POHuvpbuJU1SHmUg82VwXEH2TL87pKdqOql7ySzk
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;OMRYAf1MBSPdlu9M93L7TUJw7V6XSJ7J4LYuHW+P6ySD2NiJGVCx/Bakclosev44
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;oOE3jPXEcGmiCD8AyIorR+XCKSbL0sNSv5nenNmKDgijAx5axi4KrRwJYQARAQAB
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tCJEYXJlbiBMaWFuZyA8ZGFyZW5AZGFyZW5saWFuZy5jb20+iQJOBBMBCgA4FiEE
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ItogomM5PKacNTO+/L6J+tpcoeIFAmFu19ICGwMFCwkIBwIGFQoJCAsCBBYCAwEC
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;HgECF4AACgkQ/L6J+tpcoeJgwhAAt1vIRvrDQTPh465a63SlJB++8Hvyjw5jtaBF
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;wKUOd/cDWad6iN1qqJhhWWM7ljSgSwzpCu9eamGEOr+/CGxvByUxWVVyIm+GjyNV
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bA/bZRUrYzJnSDTe81i5effbECVfpZ4ztu86blvWpS6NKECM2mLGSyv1v9AACBvD
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ZOvpsLCk8M9RhaOEC/ySxPxSyrgzCzATgoLuVsSWhXTO4BzSAriefJYupeqjFQiB
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;4+dZE4MkB7mpFLlQkXVD/KazuVI1ZDKXhj8r1cd9Or0AW+qUq6rKzRx0aIucW+AO
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tsjmOc6yPddPMJaYggKT8vOVKMhrJRVCvHCZvTXhqahBx5QRE7C4xcfN22QaYWYb
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pGNdwq1JPh7jFplURwo0BJmIHWUpXAkXQIODuZ36rfmyUSeoxUcBGx5HdCtG2u+L
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;SSnFAtjHNOa88nItCD/MDgUKd21qqDTcsntK/3hD6XxFKQF99SKvgm3A1/5aAKpa
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;6jmBfbGRLaa1yjZHYCh2TkZ/+/ilIcSUil3Acn7LxNwVw5/aTcKKcVzR+BKAwqkr
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;p/IOOg45r4Fk56e2JBca/r5z9BnC2yjK2e6Nm4nobh4rgUkJ+U16cWAhwHKuI+Wz
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/ruf2Y3nUD4Z+STLhLpSXMgAafNDSQx2jQbDVqXU9aP35Q5LJTkeHkUBZt1nGoU/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;OwWunxO5Ag0EYW7X0gEQAMmGChDy1gNXGasgz1jdJoW0qi2XYe1/yjit+MT9OyYS
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;SrMi4q8eSHmvi4fcmWb1j/V1qbIPyNqFs/DLJ/TzWMBqvvMwycLDAwM1hOy3/HEf
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;XOtnGDdfTO+09UPMV2fhM+kG4sidFLDDLqkksJbu/cTMv5UTvlIMRVBBYB9mHZqB
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;1LaVJXXwNRcp2nWn+QPA0QQv03aDMQXfXmnmi/hHBFeYjcrBv3Jgid1YCRo/+vxF
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;3uCsupP7ZJBrupR/NXZvFDvR0WZJT1lmdy48FkdiQbCKyxflb4Wy5M2ppfzWJWCY
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/1rMXuUsNshIph64Xx/mqdroY1eKimdq+Nh5VPX5OGsgrJTAD72XvehWZkU6h9Pf
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;z/yBiNB/Wp3y7LX9Ijw15jDHfYmVHP3ruu7HAElauI2ROFDDweKWmsIlmfQ2mMuo
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bOkMo6Z5uDgSzUiz1Hyu4RLk8EyFQ6GGlYFNIA4It28WfFgV4mazIk5Pwc5wWQUD
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;W5trFm4m5BaaqOom0EoCpEaSk07dPRy1pUJvhcNWODXzViuuHoGUMfiQXb3zu8M4
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;7rK82ORK3FsrgksEnqhdb1jBWA3PIXO5QRRf/ZOsJpTtMEtiSC7nYFpgGmjN3/Bp
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;SSOfeMI6hwjmzyItUXJrHmISJbXivTqdgDXreyGWNkgjcvUUu+AJaOhG8qvBZwn3
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ABEBAAGJAjYEGAEKACAWIQQi2iCiYzk8ppw1M778von62lyh4gUCYW7X0gIbDAAK
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;CRD8von62lyh4hz4D/wN2+sDW2fjdLuJ2iUPI+3VtuP6uE4FMKUCCVH+plc3WRyO
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;9rbSRmdJ0odSe0/JD7PhZnPjj8kzwTRUv96spaCBhNjb9rzM+B2Y1fkMeOqtJp8P
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;93GyagChVnSb6ZBDdvX/i1tuU4l+2Vh7+KUAXLw70Om6EPZHcDtAxz/OMkkncomH
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ACJHmAgJpu9CkUWN1iux4oCPha/uLwFMG3/uNlDQSlbwPECMmwa14UycV1m1DSZK
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;R8rwPqtNc3t/EUiq3hsaQSQj1GWmZkUQRCNifgoVsgEIo4y/aO713geibjza5c7M
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;jr8pE70S32UMvQ1MRwyYjQKvEYtM3RBluVOgNhlz21okzJS7aXra0xapTD0nkWrE
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;d1IYjDENs/FiKgmOIdLGn1Yc3461pvLxQxYP8GtyEh3beinX6MgraXOZfxIUmUBY
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;QTYZT3KRGTZJwhyi/trB5ehFBOGqVCmc06GtQOCozzl4/B37Fejh234zANQKmDs5
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;VFS7j2JM+1PO30RRHbdwenQaurq6ZeazFrxqXMj5CYxHz3NdVD+WvOZ1vQ867uH9
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;qrJKZpbr+fl5i8BzrO4KG5SjzR+03P0rYCU2UuDUKQjgvOEFg/I2B0PiuR/vigw0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;R4vDKM7wSmHW4q0RD4o3VIr/aktZt1JNpkd/Nuqi0TDsmMze1CC+Kfgo8OSObA==
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;=Zra5
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-----END PGP PUBLIC KEY BLOCK-----
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Kelly Criterion</title><link>/demos/kelly-criterion/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>/demos/kelly-criterion/</guid><description>&lt;p&gt;The Kelly Criterion is a formula that determines the optimal theoretical size for a bet.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;General formula:&lt;/p&gt;
&lt;p&gt;
 \[
 f^* = p - \frac{q}{b} = p + \frac{p-1}{b}
 \]
&lt;/p&gt;
where:
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;\( f^* \): is the fraction of the current bankroll to wager&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;\( p \): is the probability of a win&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;\( q \): is the probability of a loss \( (q = 1 - p) \)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;\( b \): is the proportion of the bet gained with a win (\( 1 \) means you will gain the same amount you bet)&lt;/p&gt;</description></item><item><title>Projects</title><link>/projects/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>/projects/</guid><description>&lt;script async src="https://buttons.github.io/buttons.js"&gt;&lt;/script&gt;
&lt;p align="center"&gt;
&lt;picture&gt;
 &lt;source srcset="/img/imglabs/logo_with_wordmark.svg" media="(prefers-color-scheme:dark)"&gt;
 &lt;img alt="Imglabs.io" width="500" src="/img/imglabs/logo_with_wordmark_black.svg" /&gt;
&lt;/picture&gt;
&lt;/p&gt;
&lt;h2 style="text-align: center"&gt;Imglabs.io&lt;/h2&gt;
&lt;p style="text-align: center"&gt;Proxy, cache, and transform images at the network edge&lt;/p&gt;
&lt;div style="text-align:center"&gt;&lt;a href="https://www.imglabs.io"&gt;Website&lt;/a&gt;&lt;/div&gt;
&lt;br/&gt;
&lt;hr/&gt;
&lt;br/&gt;
&lt;p align="center" style="font-size: 100px; margin: 60px"&gt;🃏&lt;/p&gt;
&lt;h2 style="text-align: center"&gt;The Gang&lt;/h2&gt;
&lt;p style="text-align: center"&gt;Cooperative Poker Game by John Cooper and Kory Heath&lt;/p&gt;
&lt;div style="text-align:center"&gt;&lt;a href="https://thegang.beats.moe/"&gt;Website&lt;/a&gt;&lt;/div&gt;
&lt;br/&gt;
&lt;hr/&gt;
&lt;br/&gt;
&lt;p align="center"&gt;&lt;img alt="MikuBot" style="border-radius: 8px;" src="/img/MikuBotIconSmall.png" /&gt;&lt;/p&gt;
&lt;h2 style="text-align: center"&gt;MikuBot&lt;/h2&gt;
&lt;p style="text-align: center"&gt;Modular anime Discord bot built for weebs&lt;/p&gt;
&lt;div style="text-align: center"&gt;&lt;a class="github-button" href="https://github.com/darenliang/MikuBot" data-size="large" aria-label="Visit darenliang/MikuBot on GitHub"&gt;MikuBot&lt;/a&gt;&lt;/div&gt;
&lt;br/&gt;
&lt;hr/&gt;
&lt;br/&gt;
&lt;p align="center"&gt;&lt;img alt="nikel" style="border-radius: 8px;" src="/img/nikel.png" /&gt;&lt;/p&gt;
&lt;h2 style="text-align: center"&gt;Nikel API&lt;/h2&gt;
&lt;p style="text-align: center"&gt;Data API for the University of Toronto&lt;/p&gt;
&lt;div style="text-align: center"&gt;&lt;a class="github-button" href="https://github.com/nikel-api/nikel" data-size="large" aria-label="Visit nikel-api/nikel on GitHub"&gt;nikel&lt;/a&gt;&lt;/div&gt;
&lt;br/&gt;
&lt;hr/&gt;
&lt;br/&gt;
&lt;p align="center"&gt;&lt;img alt="dsfs" style="border-radius: 8px;" src="/img/fuse/demo.gif"&gt;&lt;/p&gt;</description></item></channel></rss>