how i accidentally turned my dota cosmetic addiction into a side hustle (and how you probably can too)
▲ 1 r/DOTA

how i accidentally turned my dota cosmetic addiction into a side hustle (and how you probably can too)

okay so this is gonna sound like cope, but i'm like 80% serious — i've spent... a lot of money on dota cosmetics over the years. we're talking hundreds. arcanas i stopped using, sets that fell off, the whole dopamine treadmill of battle pass levels. and i was just sitting here one night looking at my inventory thinking "this is just dead money. i could've literally bought a graphics card with this."

but then i realized something. the cosmetics market in dota is actually functional in a way most people don't exploit.

the thing nobody talks about:

if you're buying cosmetics with the expectation that you'll never get value back, you're playing the game wrong. and i don't mean "sell everything and become a trader" — i mean be intentional about what sticks around and what you can move.

here's the pattern i started using:

  1. buy the thing you actually want — sounds obvious but most people impulse buy during battle pass hype. wait a week. if you still want it, grab it. if you forgot about it, you just saved money.
  2. hold limited stuff — immortals, especially from older treasure lists, actually go up in value as they rotate out of the pool. i bought a kinetic gem for lich support back in like 2019 for $8. checked the market three months ago — $32. i'm not saying flip everything, but some things just age well.
  3. know what actually depreciates — ultra rare arcana drops from the current battle pass? yeah that's gonna sit at half price in a month. very rare sets from this season? everyone has them. they're worthless to trade but you won't use them either, so just... don't buy those.
  4. the secret sauce: trade with people who just want the thing — there's a whole community of dota players on discord and reddit who will trade sets for other sets at reasonable rates. like, i bought a set i thought was fire, played it twice, and traded it for something i actually loved. no net cost. happens all the time if you're not trying to rip people off.
  5. use the steam market for micro-recovery — rare drops, voiceline drops, baby roshan couriers that dropped — throw those on the market. it's not much per item but i've genuinely recouped like $15-20 a month just from things i was gonna delete anyway.

the actual number:

i've spent roughly $600 on cosmetics since 2016. by being deliberate about what i keep, knowing which treasures actually hold value, and not being afraid to trade or market shit i'm done with... i've recouped maybe $180. so my actual cosmetics cost is $420 over eight years. that's like $5 a month for the satisfaction of looking fresh in a game i play hundreds of hours in.

is that a "side hustle"? not really. is it enough to offset the dopamine purchase impulse? yeah, actually.

but here's the real talk:

the reason this works is because there's an actual community that cares about this stuff. people who hunt for specific sets, people who want to complete a hero's cosmetic collection, people who'll trade good value because they're not trying to make bank — they just want the specific thing they love.

that's what makes dota cosmetics different from like... fortnite skins or valorant shit. there's a real secondary market because people keep playing dota and keep collecting. the stuff doesn't just evaporate after a battle pass ends.

if you wanna actually recover costs:

  • join the dota cosmetics trading communities (there's discord servers dedicated to this)
  • check price history on dota2.fandom.com before you buy anything new
  • don't buy "limited" stuff that's gonna be rereleased in 6 months (valve does this constantly now)
  • sell old drops immediately — they only go down
  • hold immortals, especially rare/very rare from old treasures
  • trade with the community instead of just sitting on stuff you don't use

it's not rocket science, it's just... actually thinking about your cosmetics as items instead of just consumable battle pass content.

anyway yeah. dota cosmetics don't have to be pure sunk cost if you give a shit. and if you wanna track what's actually worth keeping vs. what to move, i built dota companion (https://www.youtube.com/@DHSeaDev has some walkthroughs) which includes inventory tracking tools that show you what stuff actually trends toward in value. not a silver bullet but it helps you see patterns.

this is genuinely something i wish i'd known earlier. would've saved me like $200 just by being slightly smarter about what i bought.

edit: "isn't this just gambling" — nah because the actual game doesn't care. you're not betting on cosmetics affecting gameplay. you're just... being deliberate about a purchase decision like you would with anything else. the difference between impulse buying and actual strategy is like... one spreadsheet and thirty seconds of thinking.

edit 2: someone asked about banned accounts and cosmetics — yeah if you get VAC'd you lose everything. don't cheat lol. but also if you're nervous about account security at least know what's actually valuable in your inventory so if you get hacked you know what to recover.

edit 3: "but isn't the market saturated now" — yeah it's different than 2018 when you could flip shit for profit. that's why i'm not calling this a hustle, just... cost recovery. the good margins are gone. the ability to not lose money on cosmetics? still there if you're smart.

u/DHSeaDev — 3 days ago
▲ 0 r/ffmpeg

PSA: `-af apad` with no `whole_dur` pads forever. It turned my 2-second test clip into a 12,662-second file.

Posting this because the fix for one bug handed me a worse one, and the failure is completely

silent until you look at the duration.

I was muxing a narration track onto a finished 55.5s render. The obvious command is:

```bash

ffmpeg -i video.mp4 -i vo.wav -c:v copy -c:a aac -shortest out.mp4

```

`-shortest` is the trap everyone warns about — if your audio is even slightly short, it truncates

the *video* to match and you silently lose the end of your film. I'd already been bitten by that

one: it ate 1.25 seconds off an outro and produced a file that played perfectly and passed every

check I had.

So I did what the docs and most StackOverflow answers suggest: drop `-shortest`, pad the audio

instead.

```bash

ffmpeg -i video.mp4 -i vo.wav -c:v copy -c:a aac -af apad out.mp4

```

**This never terminates.** Bare `apad` pads with silence indefinitely. `-shortest` was the only

thing bounding it. Remove one, you arm the other.

I didn't notice at first because it *looks* like it's working — it writes a valid growing MP4. I

killed it at the 10-minute mark and probed the output:

```

size = 120,529,993 bytes

video = 55.500 s

audio = 284,615.765 s <-- 79 hours of silence

```

Reduced to a known-answer case so it's easy to confirm (ffmpeg 6.1.1):

```bash

ffmpeg -f lavfi -i testsrc=size=320x240:rate=30 -t 2 -pix_fmt yuv420p v.mp4

ffmpeg -f lavfi -i "sine=frequency=440" -t 1 a.wav

timeout 25 ffmpeg -i v.mp4 -i a.wav -c:v copy -c:a aac -af apad old.mp4

```

2-second video in. Result:

```

exit = 124 (killed by timeout — it was not going to stop)

dur = 12,662.748 s

```

### The fix

Give the pad an explicit endpoint. Probe the video, feed the number in:

```bash

V=$(ffprobe -v error -show_entries format=duration -of csv=p=0 video.mp4)

ffmpeg -i video.mp4 -i vo.wav -c:v copy -c:a aac -af "apad=whole_dur=$V" out.mp4

```

Or pad the audio during assembly and mux with **no** `-af` at all — better if you want to assert

the voice track's length independently before it ever reaches the mux:

```bash

ffmpeg -i vo.wav -af "apad=whole_dur=$V" -c:a pcm_s16le vo_padded.wav

ffprobe -v error -show_entries format=duration -of csv=p=0 vo_padded.wav # assert this

ffmpeg -i video.mp4 -i vo_padded.wav -c:v copy -c:a aac out.mp4

```

Both land on exactly 2.000000 in the test case and exactly 55.500 on the real film.

### The actual lesson

`-shortest` and `apad` are the same bug class: **flags that silently decide where your output

ends.** One truncates, one runs away. I removed the first and left the second sitting in the same

line, because I was treating it as "the `-shortest` bug" instead of "the duration-deciding-flag

bug."

If you're fixing something like this, audit the whole command, not the flag you came for.

And assert the duration afterward as an equality check, not a glance — both failure modes produce

a file that exists, has both streams, and plays:

reddit.com
u/DHSeaDev — 7 days ago
▲ 2 r/Stress

What do you do to Escape? To Feel O.K.

Recently life happened - - I sunk into computers and back into code. I'm escaping. Is it okay if you are productive if your grief? in your sorrow? Falling apart at the word "Allowed"

Today was a lot of learning, a lot of code, a lot of edits, a lot of escape. Tomorrow will be discovery. I own it now. Own it with me.

reddit.com
u/DHSeaDev — 9 days ago
▲ 2 r/phaser+1 crossposts

I made a 30-second physics arcade for Chrome where your own shots destroy your points

Prism Cascade: you throw light-orbs at crystal prisms, the shards fall into water below, and every splash throws up bubbles carrying points. The bubbles only score once they reach the counting beam at the top — and the only thing that pops them on the way up is your own orbs and your own falling debris. A popped bubble takes its points with it.

So the whole game is a timing problem disguised as a spam-click problem. Throw constantly and you shred the bubbles already climbing. Wait for a clean column and you burn seconds off a 30-second clock. The end-of-round screen shows "Banked" and "Popped away" on separate lines, which is a genuinely uncomfortable number to look at.

100 levels from a seeded generator, a 12-node ability tree, and 5 endowments you can only carry one of at a time.

It also makes zero network requests — no fetch, no XHR, no host permissions, one storage permission to remember your progress. It works on a plane.

30-second clip: https://www.youtube.com/shorts/6oBcZolFoDQ
Details and the full privacy policy: https://dhseadev.online/projects/prism-cascade/

Not on the Chrome Web Store yet — listing is in preparation. Happy to answer anything about how it's built.

u/DHSeaDev — 9 days ago
▲ 1 r/TrueDoTA2+2 crossposts

Title: I made a free browser thing that tracks whether you're actually improving, not just winning. Stuck on what to build next.

So the thing that always bugged me about stat sites is that they answer "did you win" when the question I care about is "am I getting better." In a 5-man game those are barely the same question. You can play the best game of your life and lose because someone picked Techies into a 4-protect-

  1. So the main number I built it around is an "improvement streak" — how many games in a row you beat your OWN 20-game GPM baseline. Not your team's result. Yours. It also does goals against a baseline (GPM/KDA/last hits/win rate), today's W-L, a GPM trend line, and private notes on players you run into so you remember who the actual griefers were.

It's a browser extension, so no client, no overlay, no GSI config, nothing running in the background while you play. Paste your Steam URL and it reads public match data from OpenDota. No account, no API key, notes never leave your machine. Free, and I'm not planning to monetize it.

Where I'm stuck, and why I'm posting: I don't know what's worth building next, and I'd rather ask than guess.

Things I'm considering:

- Pulling your peers list so you can tag people you've queued with without typing account IDs

- A post-game "why" tag (tilted / off-role / tired / duo'd) so after 60 games it can tell you things like "your winrate drops after 11pm" — stuff no stat site can know because you have to volunteer it

- Winrate split by role/lane

- Percentile benchmarks so "good GPM" means good *for your bracket*, not in the abstract

Questions I actually want answers to:

  1. Is improvement-streak-vs-your-own-baseline useful, or does it just feel like a made-up number to you?

  2. What do you currently open Dotabuff/STRATZ for that annoys you every time?

  3. Would you use post-game self-tagging, or is that too much friction after a loss?

Happy to hear "this already exists, use X" too — genuinely would rather know.

https://chromewebstore.google.com/detail/dota-companion/gnnamhmenhhlgjmanoofngmfeddmfbej

Not affiliated with Valve. Also: it uses OpenDota, so you need "Expose Public Match Data" on in your Dota settings or it'll show you nothing.

u/DHSeaDev — 12 days ago

Put an LLM chat on a WP site without a plugin — 481 bytes in the page, everything else external

This sub gets the "how do I add AI chat to my site" question constantly and the answer is usually a plugin that wants an API key in the WP database. Here's the version I ended up with instead, in case it's useful to anyone else.

What's actually in WordPress: one block, 481 bytes. It sets an endpoint URL on window and injects a script tag. That's it. No plugin installed, nothing in the database, no key stored in WP.

Everything else lives in one external serverless file. It serves the widget itself from a /widget.js route, so the ~19KB of markup, styles and logic never enters WP content at all. The API key is an env var on that service. Shipping a widget change is one external deploy — I don't touch the site.

Three WP-specific things that cost me time:

  • Scripts inside an HTML block get escaped at render time, and the failure is silent. Stored content stays byte-clean while the live page does nothing and the console is empty. Always check the rendered output rather than what the editor saved.
  • Ampersands in inline script text were a repeat offender for the same reason. Keeping the injected block down to 481 bytes with no logic in it sidesteps the whole class.
  • A block appended next to another does not inherit the page wrapper. Anything you insert needs to constrain its own width or it renders indented against everything else.

If a page is still classic/freeform, block-level edits refuse to target it and you're looking at a full-content rewrite. What worked better for me was editing through the REST API from the browser console on wp-admin — read the raw content, do a targeted string replace, POST it back. Surgical, no conversion needed, and the content never round-trips through a copy step.

Result: https://dhseadev.online/ask/ Full build notes: https://dhseadev.online/2026/08/06/ai-answers-desk-val-town-groq/

u/DHSeaDev — 13 days ago
▲ 8 r/TrueDoTA2+3 crossposts

Dota Companion — Value your Inventory &amp; Stats

Local-first Dota 2 stats, improvement streaks, private player notes and inventory value. No account, no API key. https://dhseadev.online/projects/dota-companion/

**You have a 62% win rate. So why do you keep losing to Spectre?**
**Value your Dota 2 inventory** — Steam Community Market prices, with a per-item breakdown and a running history.

Dota Companion answers questions your match history can't. It compares every hero's record against *your own* baseline — not against 50% — so a 51% record when you normally win 62% shows up as what it is: an eleven-point hole, not an average day.

u/DHSeaDev — 12 days ago
▲ 6 r/ShowYourApp+1 crossposts

Made a wall where the only thing you can do is quietly mark that something good happened

Hey ahh I just wanted nine panels, each one a prompt — the last thing that actually made you laugh, someone who made your day lighter, a thing you're still bad at and doing anyway. You leave a stamp and that's it. Most of the internet houses some gaslighting opinion. Mine hopes you smile a little wider.

I wanted somewhere the interaction couldn't turn into an argument, so I removed the part where you type. You get a stamp that's generated for you and stays yours if you come back.

https://dhseadev.online/positivity-wall/

u/DHSeaDev — 15 days ago

Good news, everyone! I built a Futurama-themed Chrome Extension for AI Agents 🚀 | BYOK

Good news, everyone! 👋

Like many of you, I spend a lot of time thinking about the year 3000. So, I decided to mash together two of my favorite things: Futurama and AI Agents, and built a Chrome extension called the Planet Express Lounge.

Whether you want an AI that's 40% titanium (like Bender), someone to nervously guide you through the web (like Fry), or just a bureaucrat to handle your digital paperwork (like Hermes), this extension brings that flavor to your browser.

🚀 What is the Planet Express Lounge?

It’s an open-source Chrome extension designed to let you interact with AI agents wrapped in the personalities and theme of the Planet Express crew.

  • Themed UI: Designed to feel right at home in the Futurama universe.
  • AI Agent Integration: Run tasks and chat with agents directly from your browser sidebar.
  • 100% Open Source: No hidden microtransactions or data-slurping MomCorp business practices.

🛠️ Check it out on GitHub

I’ve open-sourced the whole project. You can check out the code, install it manually, or contribute to making it even better:

👉https://github.com/DHSeaDev/planet-express-lounge

💬 Looking for Feedback!

This is a passion project, and I’d love to know what you think. What features should I add next? Which character needs to be the next AI agent profile? (Personally, I think a Scruffy agent that just says "mmhmm" and does nothing is a high priority).

Shut up and take my code! Let me know your thoughts or drop a star on the repo if you dig it. 🚀 #DHSeaDev

u/DHSeaDev — 2 months ago