Skip to content
HN On Hacker News ↗

How Block Blast harvests its players for ad money

▲ 53 points • 14 comments • by motionlessveloc • 7h ago • HN discussion ↗

Pangram verdict · v3.3

We believe that this text is a mix of AI and human-written content.

58 %

AI likelihood · overall

Mixed
42% human-written 58% AI-generated
SEGMENTS · HUMAN 4 of 19
SEGMENTS · AI 15 of 19
WORD COUNT 582
PEAK AI % 100% · §1
Analyzed
Oct 24
backend: pangram/v3.3
Segments scanned
19 windows
avg 31 words each
Distribution
42 / 58%
human / AI fraction
Verdict
Mixed
Pangram v3.3

Article text · 582 words · 19 segments analyzed

Human AI-generated
§1 AI · 100%

ayoships.com Rules downloaded from their server decide your pieces, your score and your ads. One waits for an expensive ad, then asks for a hand you can’t place.

§2 AI · 75%

Ayo OmolewaOctober 15, 2026 I posted a video about this game that got over 15 million views, so I wanted to do the full technical write-up: the code, and how you can reproduce it yourself.

§3 AI · 100%

Some context on why it matters: between January and May 2026, Block Blast made an estimated $127 million from ads. Candy Crush Saga made $81.1 million over the same months (Sensor Tower estimates).

§4 AI · 100%

Most of Block Blast isn’t in the app you install We started with the Android app, version 10.8.2. We decompiled the Java side with jadx and the native Cocos library with Ghidra, and found almost nothing interesting in the package you download from the store. Block Blast is built from small modules the code calls traits, and each trait has variants called arms. Running it on a clean Android 14 emulator with no account and watching its cache and logcat showed how it works: when the app launches, the server sends a config that says which arm of which trait you get, and the app downloads them as encrypted JavaScript.

§5 Human · 0%

The config my install got names about 700 traits.

§6 Human · 0%

My install selected 254 of them, and 101 were downloaded after install. CTAgloReviveTraitCTReviveTimeOutSkipSplashTraitClassNewDeathOptimizeTraitDeadlyToHardTraitDecreaseReviveUIShowByLimitHighScoreTraitDifferentStrategyDieTraitEndlessDeathBoardTraitFastHandPunishTraitGLBFDieReplayTraitIsCacheReviveNumTraitIsClassADReviveNoInerestingTraitIsOpenInAdvanceDeathByLowEfficiencyGiveReviveTraitIsOpenRandomToRandomNoDieTraitLevelReviveCondiTraitLevelReviveCountTraitLevelReviveTraitMABXiBaiTraitMultiClearBoostThenKillTraitRepateCPlusPlusRandomNoDeathTraitReviveBuffTraitReviveCleanBoardTraitReviveCountTraitReviveLimitTraitRewardHighValueAdHarvestTraitAdLevelTraitAdPlusMinusStrategyTraitAddClearTipsExTraitAddMoreGameTraitAdjustPercentChTraitAlgoFillSortEdgeTraitAlgoSDK_modifyBlockPosTraitAlgoSDK_useAlgoSoAloneTraitAlgoSameBlockShangZengTraitAlgoSymmetryBlockTraitBComboScoreChangeTraitBCommentDisplayTraitBPuzzleTimeTraitBReplaceBinaryTraitBackSpaceClickLimitTraitBaseplateConditionDiffTraitBaseplateLaneTraitBaseplateTraitBlockLimitClassicsTraitBlockLimitLevelTraitBlockPoolRoundAdjustV2TraitBlockPoolStrategyExperimentTraitButtonClickAddShakeTraitCTAdvertisementMultipleCallTraitCTAdvertisementNoHideTraitCTAgloNameSpecialTraitCTAgloNameTraitCTAgloStrategyTraitCTBAlgorithmGaohuibaoARGTraitCTChapterScoreComboTraitCTChapterSkinBonesTraitCTChapterWinOverTraitCTClassSkinBonesTraitCTComboDotTraitCTCommentskinWithFeedBackTraitCTDotClickGameEndInfoTraitCTDotClickUsrDataGameEndTraitCTDotDeadReportToUsrDataGameEndTraitCTDotFirstClickDataGameEndTraitCTEffectAccTraitTraitCTGBMClearTraitCTGBMEmptySurfaceGameStartDotTraitCTGameComboScoreTraitCTGameEndStatusDotTraitCTLowDeviceEffectTraitCTMergeBlocksAgloNameTraitCTMergeBlocksSkinBonesTraitCTSkinBonesTraitChapterAlgoStrategyTraitChapterCollectScoreFixedTraitChapterWinShowRainbowTraitClassGuideEndNoAwaitNextFrameTraitClassicsRandomReplaceTraitCleanSceneRandomSkinTraitCleanSceneUseSequenceSkinTraitClearBoardHeartRippleAniTraitClearBoardPlusBlockPosTraitClearBoardPlusTraitComboAddRemoveNumTraitComboModeCtrlTraitComboRainbowTraitComboScoreSplitTraitComboTimeTraitCombo_tips_lv_blockTraitComboflyScoreTraitConsecutiveLossRecoveryTraitContinueSameMoreRoundLimitTraitControlHighestScoreRhythmTraitCrossClearHardTraitCtrlAlgoLimitTimeTraitDailyPerformanceReviewTraitDailyWinStreakStateTraitDifficultChangeColorTraitEightHoursTravelTraitExceedAimScoreTraitExpendBoardScoreTraitFastNearTkxcClassTraitFastNearTkxcTraitFeatclearscreenbombTraitFeatclearscreenbombfixTraitFillCleanAlgoTraitFillExcludeTraitFirstGameScoreTraitFirstHardMoreClearAlgoTraitFiveRoundStatisticsTraitGLBFMiddleTipsViewTraitGLBFProgressTraitGLGameAdStrategyOptimizeTraitGLResultView6TraitGLShowAdsByGameTimeTraitGameInfoDisplayTraitGameLobbyNewEntryCtrlTraitGameLobbyTraitGameLobbyUnlockByBreakHighScoreTraitGameOverShowMiniGameIconTraitGateWinStreakStateTraitGpEmptyBoardRatioGuardTraitHighProgressShowBannerTraitHighScoreBreakthroughBoostTraitHighScoreLimitByUserAbilityTraitHighValueInterstitialReliefTraitHighWeightSessionQuestionReplacerTraitHoleFillPriorityTraitHotAreaAdaptScreenBottomStateTraitHotAreaClickUtilTraitInitBoardDelayTraitInitBoardSnakeProStyleProTraitInitialBoardDisableTraitIsOpenBottomCoverChangeTraitIsOpenChangeSkinTraitIsOpenClassEncourageEffectByScoreTraitIsOpenComboContinuousTraitIsOpenComboInterruptionStrategyReplaceBlockTraitIsOpenExtraReportTraitIsOpenGLHallMoreGamesPopupTraitIsOpenGlLoadAllGameBackTraitIsOpenLevelOverTipsChangeTraitIsOpenMoreCleanChangeTraitIsOpenMoreCleanTraitIsOpenNoVideoNoRevivieTraitIsOpenOperaPosTraitIsOpenRainbowPutBlocksEffectTraitIsOpenRightPutTraitIsOpenShang1ToShang3TraitIsOpenVibrateTraitIsOpenZhiJueToKunNanTraitIsOpenZiMuShangZengTraitIsPuzzleTimeTraitIsShowAchievementTraitIsStatisticalDataCollectTraitIsTravelMoreDiamondAwardTraitIsTurnAroundSurfaceTraitIsWarmhandsTraitJewelNewOutBlockTraitLDEnableNoSyncHighScoreRollingTraitLWMDualConditionHardPuzzleTraitLaneSchemeTraitLevelConfigTraitLevelExpAlgorithmTraitLevelLoopTraitLevelNoThemeTraitLevelRandomReplaceTraitLevelRemoteThemeTraitLevelWayHelpConfigTraitLevelWayHelpTraitLimitHighComboTraitLimitNoviceBlockSizeTraitLineDynamicSendTraitLoadingTieringTraitLocalThemeSaveTraitLoginTimeFirstDifficultyRatioTraitLongThinkClearAssistTraitLowFeedbackPleasurableOfferTraitLowHardSetFps30TraitLowWeightFillBlockSortAdjustAlgoTraitLowWeightFillBlockSortTraitMaturityDevelopmentV2TraitMergeBlocksIsPuzzleTimeTraitMergeBlocksLaneSchemeTraitMoreClearStopComboTraitMoreTimeToOpreateStateTraitMultiElementCollectTraitNewAbilityModelTraitNewBottomOfferTraitNewPlayerContinuePlayIncentiveTraitNewSeasonNotifyTraitNewStructTraitNewUserLevelTraitNew_player_direct_guide_to_thirdTraitNewbieScoreRegulationTraitNoBannerAdNewUserByDaysTraitOpenChangeContinueHardTraitOpenComboModelTraitOpenLevelModelTraitOriginSkinShowGreyDefultSetBtnTraitOverHighScoreClearBoardTraitParallelClearHardTraitParam230012TraitPixelBitControllerTraitPlaceBackAdaptTraitPlaceHabitAdaptPathTraitPlaytimeNoInterstitial1TraitPostGameToServerTraitPreClearScreenAnimOptimizeTraitProblemIntervalTimeRandomBetaTraitPromotionMoreGamesInBlockCtrlTraitPutBlockOptimizeTraitPuzzle100TraitPuzzle100WeightConditionTraitPuzzleAfterTimingTraitPuzzleBeforeTimingTraitPuzzleConditionDiffTraitPuzzleFail100TraitPuzzleSuccess100TraitPuzzleTiming100TraitPuzzleTimingMaxPriorityTraitRatioAdjustTraitRatioDownTraitReduceTravelSettlementTraitReplaceBoardTraitReplaceClearnScreenAniTraitRightPutHintCtrTraitRobotModelEventDataTraitSequencePredictDynamicDifficultyTraitServerControl_board_init_v2TraitSetAddDefultSkinBtnTraitSettleFireworkTraitSetupNewTraitShangZengAddRandomTraitShangzeng3ReplaceZimushangzengTraitShowSSSWhenSkinChangeTraitShowTravellClearAnimaTraitSimpleMultiSolutionTuning2TraitSkinChangeAblePushTraitSkinPreEliminateGoodPerformanceTraitSkinRandomTraitSkinThree24NewColorsTraitSkinThreeOptimizationsTraitSkinThreeStageTraitSlowPlayerAnimTraitSmallgameLocalResTraitSolidColorTraitTerminateRefreshScoreTraitTheMoreEliminateTheMoreGoodEffectTraitTravelEntryClockShadowFixTraitTravelEntryClockShadowTraitTravelHappyOverTraitTravelNoSkinTraitTravelResultOptimizingTraitTravelScoreCutoffByProgressTrait All 254 traits assigned to my Android test install.

§7 Human · 13%

The ones about losing, revives and ads are highlighted.

§8 AI · 100%

So two people playing side by side can be running different games, and the studio can change yours without an app update.

§9 AI · 100%

Most of those 254 are ordinary: tutorials, animations, sounds.

§10 AI · 100%

A handful are about when you lose.

§11 AI · 100%

One of them is about ads.

§12 AI · 99%

The rule that waits for an expensive ad It’s called RewardHighValueAdHarvestTrait.

§13 AI · 97%

By finding where the game unlocks these files in its own native library, we decoded it into 15,616 bytes of plain, minified JavaScript. Its constructor sets the parameters: RewardHighValueAdHarvestTrait.js · lines 63–67 t.HIGH_PRICE_MULTIPLIER = 3.5; t.MEDIUM_PRICE_MULTIPLIER = 1.5; t.MIN_HISTORY_AD_WATCH_COUNT = 3; t.RECENT_AD_NO_ESCAPE_COUNT = 3; t.TARGET_AD_LEVEL = "high"; It waits for three things to line up: you finished your last three rewarded ads, the ad loaded right now is worth at least 3.5 times your last one, and you’re 1,000 points past your high score.

§14 AI · 100%

All three get checked in one function.

§15 AI · 100%

Here it is with the minified null checks rewritten as optional chaining; the logic is the same: shouldTriggerDeath · lines 262–267 (reformatted) shouldTriggerDeath() { if (!this.canTrigger()) return false; // finished your last 3 rewarded ads const d = this.storageData; return d.currentGameNum === hs.classGameInfo?.gameNum // same game && !d.currentGameHasTriggeredDeath // hasn't fired yet && !(hs.classScoreInfo?.score < this.currentScoreThreshold) // past high score + 1,000 && !!this.isTargetAdReady(); // loaded ad ≥ 3.5× the last one } Here’s how it checks each one.

§16 AI · 100%

1. You finished your last three rewarded ads.

§17 AI · 100%

Every time a rewarded ad starts, the rule pushes false onto a list and keeps the latest three (lines 174–183). If the ad SDK reports that the reward was earned, it flips that entry to true (191–199).

§18 Human · 0%

The check needs all three to be exactly true: canTrigger · lines 268–275 t.prototype.canTrigger = function() { var e = this.storageData; if (e.totalRewardShowCount < this.MIN_HISTORY_AD_WATCH_COUNT) return !1; var t = e.recentRewardResults.slice(0, this.RECENT_AD_NO_ESCAPE_COUNT); return !(t.length < this.RECENT_AD_NO_ESCAPE_COUNT) && t.every(function(e) { return !0 === e; }); }; That flag comes from AppLovin MAX’s onUserRewarded callback in the Java layer, so an ad you close early stays false.

§19 AI · 98%

2.