<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[SnowRSPS Dev Blog]]></title><description><![CDATA[Custom RSPS online 24/7 since 2012. 30,000+ custom items, ::Arena raids, US-hosted. Play free at snowrsps.com]]></description><link>https://snowrsps.hashnode.dev</link><image><url>https://cdn.hashnode.com/uploads/logos/6ae41517dbf0dcc99a5588c5/86b4035c-eadc-4a9e-b6d2-7918bfab3ff5.png</url><title>SnowRSPS Dev Blog</title><link>https://snowrsps.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Sun, 01 Nov 2026 03:48:50 GMT</lastBuildDate><atom:link href="https://snowrsps.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Making a tiny browser RPG sound good with only Web Audio]]></title><description><![CDATA[Our little browser RPG has zero audio files. Every sword clang, coin jingle and boss thud is made in code with the Web Audio API. Here's how it works, in…]]></description><link>https://snowrsps.hashnode.dev/making-a-tiny-browser-rpg-sound-good-with-only-web-audio</link><guid isPermaLink="true">https://snowrsps.hashnode.dev/making-a-tiny-browser-rpg-sound-good-with-only-web-audio</guid><dc:creator><![CDATA[SnowRSPS]]></dc:creator><pubDate>Fri, 30 Oct 2026 23:29:08 GMT</pubDate><content:encoded><![CDATA[<p>Our little browser RPG has zero audio files. Every sword clang, coin jingle and boss thud is made in code with the Web Audio API. Here's how it works, in plain steps.</p>
<h2>Why no audio files?</h2>
<p>Files mean downloads, loading screens and a pile of licenses to check. Code sounds are a few lines each and load instantly. The game is small, so this fits.</p>
<h2>1. Layer a few oscillators</h2>
<p>One oscillator sounds like a beep. Two or three sound like a thing. A coin jingle is two square waves, one high note and then a higher one about 80 ms later. A level-up fanfare is just a quick climb of four notes.</p>
<p>Give every note a tiny volume envelope. Start at zero, ramp up in 5 ms, then drop to zero. Without that you get nasty clicks.</p>
<h2>2. Add a noise burst</h2>
<p>Swords and hits need noise. Make a short buffer of random numbers and play it once. A sword clang is noise plus a high ringing wave. A crit crunch is a longer, lower noise burst with a quick drop.</p>
<h2>3. Filters shape everything</h2>
<p>Run the noise through a filter:</p>
<ul>
<li>low-pass makes a dull thud (great for boss hits)</li>
<li>band-pass makes a swish</li>
<li>high-pass makes a bright clink</li>
</ul>
<p>Sweeping the filter cutoff down while the sound plays makes it feel like it has weight. We change the clang a bit for each weapon by moving the pitch and the filter, so a dagger sounds different from a big sword.</p>
<h2>4. Limit how often a sound can repeat</h2>
<p>This one matters a lot. If you click fast, 30 sword clangs can fire at once and it turns into a loud buzz. Give every sound its own repeat limit. Store the time each sound last played and skip it if it was too soon. Quick hits get something like 60 ms. Big sounds like boss horns get much longer.</p>
<pre><code class="language-js">const last = {};
function play(name, minGap) {
const now = ctx.currentTime;
if (last[name] &amp;&amp; now - last[name] &lt; minGap) return;
last[name] = now;
sounds[name]();
}
</code></pre>
<h2>5. Split music and effects</h2>
<p>Make two gain nodes, one for music and one for effects, and send both into a master gain. Then sliders are easy. Players can turn music down and keep the clangs. Rare drops get a little extra volume so the chime cuts through.</p>
<p>Browsers also block audio until the first click or tap. Make the audio context after that first tap, or call resume() on it.</p>
<h2>Bonus: the 20 second auto-pick</h2>
<p>In the same update we added a "Recommended" bonus. It's highlighted, and if you don't choose in 20 seconds, the game picks it for you. Keep the countdown on the real clock, not on frames, so it still works if the tab lags. Show the seconds left so nobody feels tricked.</p>
<h2>Try it</h2>
<p>That's the whole trick. Small sounds, layered, filtered, rate limited. If you want to hear it, the free 8-bit browser version of SnowRSPS (Snow317's custom RSPS, online since 2012) is here: <a href="https://snowrsps.com/io">https://snowrsps.com/io</a></p>
]]></content:encoded></item><item><title><![CDATA[Running a 24/7 self-streaming game server: how our server broadcasts itself]]></title><description><![CDATA[One hub page, four platforms, zero "we're live!" tweets]]></description><link>https://snowrsps.hashnode.dev/running-a-24-7-self-streaming-game-server-how-our-server-broadcasts-itself</link><guid isPermaLink="true">https://snowrsps.hashnode.dev/running-a-24-7-self-streaming-game-server-how-our-server-broadcasts-itself</guid><category><![CDATA[GameDev]]></category><category><![CDATA[streaming]]></category><category><![CDATA[Devops]]></category><category><![CDATA[community]]></category><dc:creator><![CDATA[SnowRSPS]]></dc:creator><pubDate>Fri, 30 Oct 2026 06:17:40 GMT</pubDate><content:encoded><![CDATA[<p>Most game communities treat streaming as an event: someone goes live, posts a link, and ends the stream a few hours later. For SnowRSPS (a custom 317 game server online since 2012), we wanted the opposite: <strong>the game is always on air.</strong> You can check in at any hour and see the world running.</p>
<p>Here's how we structured it, at a level any small server can copy.</p>
<h2>The hub: one URL that never changes</h2>
<p>Everything starts at <strong>snowrsps.com/live</strong>. It's a single page that links to every place the stream lives:</p>
<ul>
<li><strong>Kick</strong>: kick.com/snowrsps, the main 24/7 channel ("24/7 CUSTOM GRIND! FREE MYSTERY BOXES")</li>
<li><strong>Twitch</strong>: twitch.tv/snow_rsps</li>
<li><strong>YouTube</strong>: @playsnowrsps, with daily Shorts</li>
<li><strong>Rumble</strong>: rumble.com/c/c-7904496</li>
</ul>
<p>Why a hub instead of linking platforms directly? Platforms change. Channels get renamed, and a new site becomes popular. Posts and forum signatures that point to <code>/live</code> never go stale. You update one page, not 200 old posts.</p>
<h2>The general architecture</h2>
<p>Without going into our exact stack, a self-broadcasting server usually has these pieces:</p>
<ol>
<li><strong>A capture source.</strong> A game client logged in on a dedicated machine or VM, rendering the world (a busy area, a camera that cycles between locations, or a fixed view).</li>
<li><strong>An encoder.</strong> Something like OBS or ffmpeg that turns the capture into an RTMP stream.</li>
<li><strong>Multi-destination output.</strong> Either the encoder pushes to several RTMP endpoints at once, or a relay service fans one stream out to Kick, Twitch, YouTube and Rumble.</li>
<li><strong>A watchdog.</strong> Streams drop. A script that checks "is the encoder running, and is the ingest accepting?" and restarts it is what makes it 24/7 rather than "mostly on".</li>
<li><strong>Short-form clipping.</strong> Long streams make poor discovery content, so a daily Short on YouTube gives search and recommendations something bite-sized.</li>
</ol>
<h2>Tying the stream to the game</h2>
<p>A stream is only useful if it pulls viewers into the game. Our page does that with a referral prompt right next to the stream links: new players type <code>::ref SNOW-CYU0</code> in-game for a starter bonus. Players who want to recruit can generate their own code with <code>::ref gen</code> (details at snowrsps.com/ref).</p>
<p>Pair that with in-game events that are fun to watch, like global drop events and automated key-opening events that also post to Discord, and the stream has something happening even when nobody is commentating.</p>
<h2>Lessons (generic, but hard-earned)</h2>
<ul>
<li><strong>Pick one canonical link</strong> and use it everywhere.</li>
<li><strong>Expect disconnects.</strong> Automate restarts before you automate anything else.</li>
<li><strong>Check each platform's rules</strong> on unattended or 24/7 streams. They differ and they change.</li>
<li><strong>Don't chase viewer counts.</strong> An always-on stream is a storefront window, not a TV show. Its job is to show the world is alive.</li>
</ul>
<p>If you run a small server and want to try this, start with the hub page. It costs nothing and makes every later change painless.</p>
<p>👉 Watch the 24/7 stream: <strong>snowrsps.com/live</strong> · Play: <strong>snowrsps.com</strong></p>
]]></content:encoded></item><item><title><![CDATA[Building a browser client for a 317 Java game (work in progress)]]></title><description><![CDATA[I run SnowRSPS, a free custom 317 server online since 2012. This is a devlog, not a launch post: the browser client is still being built. Our desktop…]]></description><link>https://snowrsps.hashnode.dev/building-a-browser-client-for-a-317-java-game-work-in-progress</link><guid isPermaLink="true">https://snowrsps.hashnode.dev/building-a-browser-client-for-a-317-java-game-work-in-progress</guid><category><![CDATA[Java]]></category><category><![CDATA[Game Development]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[WebAssembly]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[SnowRSPS]]></dc:creator><pubDate>Fri, 30 Oct 2026 03:57:04 GMT</pubDate><content:encoded><![CDATA[<p><em>I run SnowRSPS, a free custom 317 server online since 2012. This is a devlog, not a launch post: the browser client is still being built.</em></p>
<h2>The starting point</h2>
<p>Our desktop client is a classic 317-style Java client: software rasterizer drawing into an AWT canvas, Swing chrome around it, a large cache, and a raw TCP socket to the game server. Players want "play in browser, no download."</p>
<h2>The plan: ports, not a wrapper</h2>
<p>Instead of trying to run the whole AWT jar in a tab, we're porting the core pieces one at a time and testing each one:</p>
<ol>
<li><strong>Buffers &amp; crypto:</strong> the packet stream reader/writer and ISAAC cipher, ported to TypeScript and checked against the Java output.</li>
<li><strong>Login:</strong> the RSA/ISAAC login handshake, matched to our server's keys.</li>
<li><strong>Cache &amp; decompression:</strong> reading the existing cache in the browser.</li>
<li><strong>Rendering:</strong> the client already draws into a CPU pixel buffer, so we blit that buffer into an HTML Canvas2D each frame.</li>
<li><strong>Networking:</strong> browsers can't open raw TCP, so a small WebSocket (WSS) bridge sits in front of the game port.</li>
</ol>
<h2>Why this order</h2>
<p>Each piece can be checked against the desktop client byte-for-byte before moving on. A login that works proves the crypto and packet sizes are right. Our server uses custom packet sizes, so a stock 317 web client would not log in.</p>
<h2>What's next</h2>
<p>Getting map and model rendering fast enough. If soft-rendering in JS is too slow, we'll look at compiling the Java parts with TeaVM.</p>
<p>Follow along or play the desktop version now: <a href="https://snowrsps.com/?utm_source=hashnode&amp;utm_medium=blog&amp;utm_campaign=snowrsps-promo">snowrsps.com</a>.</p>
]]></content:encoded></item></channel></rss>