diff --git a/Telegram/SourceFiles/boxes/gift_premium_box.cpp b/Telegram/SourceFiles/boxes/gift_premium_box.cpp
index f8ff6ec7c..35bedab59 100644
--- a/Telegram/SourceFiles/boxes/gift_premium_box.cpp
+++ b/Telegram/SourceFiles/boxes/gift_premium_box.cpp
@@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "settings/settings_premium.h"
 #include "ui/basic_click_handlers.h" // UrlClickHandler::Open.
 #include "ui/effects/premium_graphics.h"
+#include "ui/effects/premium_stars.h"
 #include "ui/layers/generic_box.h"
 #include "ui/special_buttons.h"
 #include "ui/text/format_values.h"
@@ -89,6 +90,76 @@ GiftOptions GiftOptionFromTL(
 	return result;
 }
 
+class ColoredMiniStars final {
+public:
+	ColoredMiniStars(not_null<Ui::RpWidget*> parent);
+
+	void setSize(const QSize &size);
+	void setPosition(QPoint position);
+	void paint(Painter &p);
+
+private:
+	Ui::Premium::MiniStars _ministars;
+	QRectF _ministarsRect;
+	QImage _frame;
+	QImage _mask;
+	QSize _size;
+	QPoint _position;
+
+};
+
+ColoredMiniStars::ColoredMiniStars(not_null<Ui::RpWidget*> parent)
+: _ministars([=](const QRect &r) {
+	parent->update(r.translated(_position));
+}, true) {
+}
+
+void ColoredMiniStars::setSize(const QSize &size) {
+	_frame = QImage(
+		size * style::DevicePixelRatio(),
+		QImage::Format_ARGB32_Premultiplied);
+	_frame.setDevicePixelRatio(style::DevicePixelRatio());
+
+	_mask = _frame;
+	_mask.fill(Qt::transparent);
+	{
+		Painter p(&_mask);
+		auto gradient = QLinearGradient(0, 0, size.width(), 0);
+		gradient.setStops(Ui::Premium::GiftGradientStops());
+		p.setPen(Qt::NoPen);
+		p.setBrush(gradient);
+		p.drawRect(0, 0, size.width(), size.height());
+	}
+
+	_size = size;
+
+	{
+		const auto s = _size / Ui::Premium::MiniStars::kSizeFactor;
+		const auto margins = QMarginsF(
+			s.width() / 2.,
+			s.height() / 2.,
+			s.width() / 2.,
+			s.height() / 2.);
+		_ministarsRect = QRectF(QPointF(), _size) - margins;
+	}
+}
+
+void ColoredMiniStars::setPosition(QPoint position) {
+	_position = std::move(position);
+}
+
+void ColoredMiniStars::paint(Painter &p) {
+	_frame.fill(Qt::transparent);
+	{
+		Painter q(&_frame);
+		_ministars.paint(q, _ministarsRect);
+		q.setCompositionMode(QPainter::CompositionMode_SourceIn);
+		q.drawImage(0, 0, _mask);
+	}
+
+	p.drawImage(_position, _frame);
+}
+
 void GiftBox(
 		not_null<Ui::GenericBox*> box,
 		not_null<Window::SessionController*> controller,
@@ -111,6 +182,8 @@ void GiftBox(
 			+ userpicPadding.bottom()
 			+ st::defaultUserpicButton.size.height()));
 
+	const auto stars = box->lifetime().make_state<ColoredMiniStars>(top);
+
 	const auto userpic = Ui::CreateChild<Ui::UserpicButton>(
 		top,
 		user,
@@ -122,8 +195,26 @@ void GiftBox(
 		userpic->moveToLeft(
 			(width - userpic->width()) / 2,
 			userpicPadding.top());
+
+		const auto center = top->rect().center();
+		const auto size = QSize(
+			userpic->width() * Ui::Premium::MiniStars::kSizeFactor,
+			userpic->height());
+		const auto ministarsRect = QRect(
+			QPoint(center.x() - size.width(), center.y() - size.height()),
+			QPoint(center.x() + size.width(), center.y() + size.height()));
+		stars->setPosition(ministarsRect.topLeft());
+		stars->setSize(ministarsRect.size());
 	}, userpic->lifetime());
 
+	top->paintRequest(
+	) | rpl::start_with_next([=](const QRect &r) {
+		Painter p(top);
+
+		p.fillRect(r, Qt::transparent);
+		stars->paint(p);
+	}, top->lifetime());
+
 	const auto close = Ui::CreateChild<Ui::IconButton>(
 		buttonsParent,
 		st::infoTopBarClose);
diff --git a/Telegram/SourceFiles/settings/settings_premium.cpp b/Telegram/SourceFiles/settings/settings_premium.cpp
index 1e7d68f80..3afde7749 100644
--- a/Telegram/SourceFiles/settings/settings_premium.cpp
+++ b/Telegram/SourceFiles/settings/settings_premium.cpp
@@ -7,7 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #include "settings/settings_premium.h"
 
-#include "base/random.h"
 #include "core/application.h"
 #include "core/click_handler_types.h"
 #include "data/data_peer_values.h"
@@ -19,9 +18,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "settings/settings_premium.h"
 #include "ui/abstract_button.h"
 #include "ui/basic_click_handlers.h"
-#include "ui/effects/animation_value_f.h"
 #include "ui/effects/gradient.h"
 #include "ui/effects/premium_graphics.h"
+#include "ui/effects/premium_stars.h"
 #include "ui/text/text_utilities.h"
 #include "ui/text/format_values.h"
 #include "ui/layers/generic_box.h"
@@ -46,8 +45,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "styles/style_layers.h"
 #include "styles/style_settings.h"
 
-#include <QSvgRenderer>
-
 namespace Settings {
 namespace {
 
@@ -58,8 +55,6 @@ constexpr auto kTitleAnimationPart = 0.15;
 
 constexpr auto kTitleAdditionalScale = 0.15;
 
-constexpr auto kDeformationMax = 0.1;
-
 struct Entry {
 	const style::icon *icon;
 	rpl::producer<QString> title;
@@ -230,187 +225,6 @@ void SendScreenAccept(not_null<Window::SessionController*> controller) {
 		MTP_jsonNull());
 }
 
-class MiniStars final {
-public:
-	MiniStars(Fn<void(const QRect &r)> updateCallback);
-
-	void paint(Painter &p, const QRectF &rect);
-	void setPaused(bool paused);
-
-private:
-	struct MiniStar {
-		crl::time birthTime = 0;
-		crl::time deathTime = 0;
-		int angle = 0;
-		float64 size = 0.;
-		float64 alpha = 0.;
-		float64 sinFactor = 0.;
-	};
-
-	struct Interval {
-		int from = 0;
-		int length = 0;
-	};
-
-	void createStar(crl::time now);
-	[[nodiscard]] int angle() const;
-	[[nodiscard]] crl::time timeNow() const;
-	[[nodiscard]] int randomInterval(const Interval &interval) const;
-
-	const std::vector<Interval> _availableAngles;
-	const Interval _lifeLength;
-	const Interval _deathTime;
-	const Interval _size;
-	const Interval _alpha;
-	const Interval _sinFactor;
-
-	const float64 _appearProgressTill;
-	const float64 _disappearProgressAfter;
-	const float64 _distanceProgressStart;
-
-	QSvgRenderer _sprite;
-
-	Ui::Animations::Basic _animation;
-
-	std::vector<MiniStar> _ministars;
-
-	crl::time _nextBirthTime = 0;
-	bool _paused = false;
-
-	QRect _rectToUpdate;
-
-};
-
-MiniStars::MiniStars(Fn<void(const QRect &r)> updateCallback)
-: _availableAngles({
-	Interval{ -10, 40 },
-	Interval{ 180 + 10 - 40, 40 },
-	Interval{ 180 + 15, 50 },
-	Interval{ -15 - 50, 50 },
-})
-, _lifeLength({ 150, 200 })
-, _deathTime({ 1500, 2000 })
-, _size({ 10, 20 })
-, _alpha({ 40, 60 })
-, _sinFactor({ 10, 190 })
-, _appearProgressTill(0.2)
-, _disappearProgressAfter(0.8)
-, _distanceProgressStart(0.5)
-, _sprite(u":/gui/icons/settings/starmini.svg"_q)
-, _animation([=](crl::time now) {
-	if (now > _nextBirthTime && !_paused) {
-		createStar(now);
-		_nextBirthTime = now + randomInterval(_lifeLength);
-	}
-	if (_rectToUpdate.isValid()) {
-		updateCallback(base::take(_rectToUpdate));
-	}
-}) {
-	if (anim::Disabled()) {
-		const auto from = _deathTime.from + _deathTime.length;
-		for (auto i = -from; i < 0; i += randomInterval(_lifeLength)) {
-			createStar(i);
-		}
-		updateCallback(_rectToUpdate);
-	} else {
-		_animation.start();
-	}
-}
-
-int MiniStars::randomInterval(const Interval &interval) const {
-	return interval.from + base::RandomIndex(interval.length);
-}
-
-crl::time MiniStars::timeNow() const {
-	return anim::Disabled() ? 0 : crl::now();
-}
-
-void MiniStars::paint(Painter &p, const QRectF &rect) {
-	const auto center = rect.center();
-	const auto opacity = p.opacity();
-	for (const auto &ministar : _ministars) {
-		const auto progress = (timeNow() - ministar.birthTime)
-			/ float64(ministar.deathTime - ministar.birthTime);
-		if (progress > 1.) {
-			continue;
-		}
-		const auto appearProgress = std::clamp(
-			progress / _appearProgressTill,
-			0.,
-			1.);
-		const auto rsin = float(std::sin(ministar.angle * M_PI / 180.));
-		const auto rcos = float(std::cos(ministar.angle * M_PI / 180.));
-		const auto end = QPointF(
-			rect.width() / 1.5 * rcos,
-			rect.height() / 1.5 * rsin);
-
-		const auto alphaProgress = 1.
-			- (std::clamp(progress - _disappearProgressAfter, 0., 1.)
-				/ (1. - _disappearProgressAfter));
-		p.setOpacity(ministar.alpha
-			* alphaProgress
-			* appearProgress
-			* opacity);
-
-		const auto deformResult = progress * 360;
-		const auto rsinDeform = float(
-			std::sin(ministar.sinFactor * deformResult * M_PI / 180.));
-		const auto deformH = 1. + kDeformationMax * rsinDeform;
-		const auto deformW = 1. / deformH;
-
-		const auto distanceProgress = _distanceProgressStart + progress;
-		const auto starSide = ministar.size * appearProgress;
-		const auto widthFade = (std::abs(rcos) >= std::abs(rsin));
-		const auto starWidth = starSide
-			* (widthFade ? alphaProgress : 1.)
-			* deformW;
-		const auto starHeight = starSide
-			* (!widthFade ? alphaProgress : 1.)
-			* deformH;
-		const auto renderRect = QRectF(
-			center.x()
-				+ anim::interpolateF(0, end.x(), distanceProgress)
-				- starWidth / 2.,
-			center.y()
-				+ anim::interpolateF(0, end.y(), distanceProgress)
-				- starHeight / 2.,
-			starWidth,
-			starHeight);
-		_sprite.render(&p, renderRect);
-		_rectToUpdate |= renderRect.toRect();
-	}
-	p.setOpacity(opacity);
-}
-
-void MiniStars::setPaused(bool paused) {
-	_paused = paused;
-}
-
-int MiniStars::angle() const {
-	const auto &interval = _availableAngles[
-		base::RandomIndex(_availableAngles.size())];
-	return base::RandomIndex(interval.length) + interval.from;
-}
-
-void MiniStars::createStar(crl::time now) {
-	auto ministar = MiniStar{
-		.birthTime = now,
-		.deathTime = now + randomInterval(_deathTime),
-		.angle = angle(),
-		.size = float64(randomInterval(_size)),
-		.alpha = float64(randomInterval(_alpha)) / 100.,
-		.sinFactor = randomInterval(_sinFactor) / 100.
-			* (base::RandomIndex(2) == 1 ? 1. : -1.),
-	};
-	for (auto i = 0; i < _ministars.size(); i++) {
-		if (ministar.birthTime > _ministars[i].deathTime) {
-			_ministars[i] = ministar;
-			return;
-		}
-	}
-	_ministars.push_back(ministar);
-}
-
 class TopBar final : public Ui::RpWidget {
 public:
 	TopBar(
@@ -435,7 +249,7 @@ private:
 	const style::font &_titleFont;
 	const style::margins &_titlePadding;
 	object_ptr<Ui::FlatLabel> _about;
-	MiniStars _ministars;
+	Ui::Premium::MiniStars _ministars;
 	QSvgRenderer _star;
 
 	struct {
diff --git a/Telegram/SourceFiles/ui/effects/premium_stars.cpp b/Telegram/SourceFiles/ui/effects/premium_stars.cpp
new file mode 100644
index 000000000..ff5c20e75
--- /dev/null
+++ b/Telegram/SourceFiles/ui/effects/premium_stars.cpp
@@ -0,0 +1,150 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#include "ui/effects/premium_stars.h"
+
+#include "base/random.h"
+#include "ui/effects/animation_value_f.h"
+
+namespace Ui {
+namespace Premium {
+
+constexpr auto kDeformationMax = 0.1;
+
+MiniStars::MiniStars(Fn<void(const QRect &r)> updateCallback, bool opaque)
+: _availableAngles({
+	Interval{ -10, 40 },
+	Interval{ 180 + 10 - 40, 40 },
+	Interval{ 180 + 15, 50 },
+	Interval{ -15 - 50, 50 },
+})
+, _lifeLength({ 150, 200 })
+, _deathTime({ 1500, 2000 })
+, _size({ 10, 20 })
+, _alpha({ opaque ? 100 : 40, opaque ? 100 : 60 })
+, _sinFactor({ 10, 190 })
+, _appearProgressTill(0.2)
+, _disappearProgressAfter(0.8)
+, _distanceProgressStart(0.5)
+, _sprite(u":/gui/icons/settings/starmini.svg"_q)
+, _animation([=](crl::time now) {
+	if (now > _nextBirthTime && !_paused) {
+		createStar(now);
+		_nextBirthTime = now + randomInterval(_lifeLength);
+	}
+	if (_rectToUpdate.isValid()) {
+		updateCallback(base::take(_rectToUpdate));
+	}
+}) {
+	if (anim::Disabled()) {
+		const auto from = _deathTime.from + _deathTime.length;
+		for (auto i = -from; i < 0; i += randomInterval(_lifeLength)) {
+			createStar(i);
+		}
+		updateCallback(_rectToUpdate);
+	} else {
+		_animation.start();
+	}
+}
+
+int MiniStars::randomInterval(const Interval &interval) const {
+	return interval.from + base::RandomIndex(interval.length);
+}
+
+crl::time MiniStars::timeNow() const {
+	return anim::Disabled() ? 0 : crl::now();
+}
+
+void MiniStars::paint(Painter &p, const QRectF &rect) {
+	const auto center = rect.center();
+	const auto opacity = p.opacity();
+	for (const auto &ministar : _ministars) {
+		const auto progress = (timeNow() - ministar.birthTime)
+			/ float64(ministar.deathTime - ministar.birthTime);
+		if (progress > 1.) {
+			continue;
+		}
+		const auto appearProgress = std::clamp(
+			progress / _appearProgressTill,
+			0.,
+			1.);
+		const auto rsin = float(std::sin(ministar.angle * M_PI / 180.));
+		const auto rcos = float(std::cos(ministar.angle * M_PI / 180.));
+		const auto end = QPointF(
+			rect.width() / kSizeFactor * rcos,
+			rect.height() / kSizeFactor * rsin);
+
+		const auto alphaProgress = 1.
+			- (std::clamp(progress - _disappearProgressAfter, 0., 1.)
+				/ (1. - _disappearProgressAfter));
+		p.setOpacity(ministar.alpha
+			* alphaProgress
+			* appearProgress
+			* opacity);
+
+		const auto deformResult = progress * 360;
+		const auto rsinDeform = float(
+			std::sin(ministar.sinFactor * deformResult * M_PI / 180.));
+		const auto deformH = 1. + kDeformationMax * rsinDeform;
+		const auto deformW = 1. / deformH;
+
+		const auto distanceProgress = _distanceProgressStart + progress;
+		const auto starSide = ministar.size * appearProgress;
+		const auto widthFade = (std::abs(rcos) >= std::abs(rsin));
+		const auto starWidth = starSide
+			* (widthFade ? alphaProgress : 1.)
+			* deformW;
+		const auto starHeight = starSide
+			* (!widthFade ? alphaProgress : 1.)
+			* deformH;
+		const auto renderRect = QRectF(
+			center.x()
+				+ anim::interpolateF(0, end.x(), distanceProgress)
+				- starWidth / 2.,
+			center.y()
+				+ anim::interpolateF(0, end.y(), distanceProgress)
+				- starHeight / 2.,
+			starWidth,
+			starHeight);
+		_sprite.render(&p, renderRect);
+		_rectToUpdate |= renderRect.toRect();
+	}
+	p.setOpacity(opacity);
+}
+
+void MiniStars::setPaused(bool paused) {
+	_paused = paused;
+}
+
+int MiniStars::angle() const {
+	const auto &interval = _availableAngles[
+		base::RandomIndex(_availableAngles.size())];
+	return base::RandomIndex(interval.length) + interval.from;
+}
+
+void MiniStars::createStar(crl::time now) {
+	auto ministar = MiniStar{
+		.birthTime = now,
+		.deathTime = now + randomInterval(_deathTime),
+		.angle = angle(),
+		.size = float64(randomInterval(_size)),
+		.alpha = float64(randomInterval(_alpha)) / 100.,
+		.sinFactor = randomInterval(_sinFactor) / 100.
+			* (base::RandomIndex(2) == 1 ? 1. : -1.),
+	};
+	for (auto i = 0; i < _ministars.size(); i++) {
+		if (ministar.birthTime > _ministars[i].deathTime) {
+			_ministars[i] = ministar;
+			return;
+		}
+	}
+	_ministars.push_back(ministar);
+}
+
+
+} // namespace Premium
+} // namespace Ui
diff --git a/Telegram/SourceFiles/ui/effects/premium_stars.h b/Telegram/SourceFiles/ui/effects/premium_stars.h
new file mode 100644
index 000000000..be71db324
--- /dev/null
+++ b/Telegram/SourceFiles/ui/effects/premium_stars.h
@@ -0,0 +1,71 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#pragma once
+
+#include "ui/effects/animations.h"
+
+#include <QSvgRenderer>
+
+namespace Ui {
+namespace Premium {
+
+class MiniStars final {
+public:
+	MiniStars(Fn<void(const QRect &r)> updateCallback, bool opaque = false);
+
+	void paint(Painter &p, const QRectF &rect);
+	void setPaused(bool paused);
+
+	static constexpr auto kSizeFactor = 1.5;
+
+private:
+	struct MiniStar {
+		crl::time birthTime = 0;
+		crl::time deathTime = 0;
+		int angle = 0;
+		float64 size = 0.;
+		float64 alpha = 0.;
+		float64 sinFactor = 0.;
+	};
+
+	struct Interval {
+		int from = 0;
+		int length = 0;
+	};
+
+	void createStar(crl::time now);
+	[[nodiscard]] int angle() const;
+	[[nodiscard]] crl::time timeNow() const;
+	[[nodiscard]] int randomInterval(const Interval &interval) const;
+
+	const std::vector<Interval> _availableAngles;
+	const Interval _lifeLength;
+	const Interval _deathTime;
+	const Interval _size;
+	const Interval _alpha;
+	const Interval _sinFactor;
+
+	const float64 _appearProgressTill;
+	const float64 _disappearProgressAfter;
+	const float64 _distanceProgressStart;
+
+	QSvgRenderer _sprite;
+
+	Ui::Animations::Basic _animation;
+
+	std::vector<MiniStar> _ministars;
+
+	crl::time _nextBirthTime = 0;
+	bool _paused = false;
+
+	QRect _rectToUpdate;
+
+};
+
+} // namespace Premium
+} // namespace Ui
diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake
index 9724f709a..79ae20773 100644
--- a/Telegram/cmake/td_ui.cmake
+++ b/Telegram/cmake/td_ui.cmake
@@ -229,6 +229,8 @@ PRIVATE
     ui/effects/fireworks_animation.h
     ui/effects/premium_graphics.cpp
     ui/effects/premium_graphics.h
+    ui/effects/premium_stars.cpp
+    ui/effects/premium_stars.h
     ui/effects/round_checkbox.cpp
     ui/effects/round_checkbox.h
     ui/effects/scroll_content_shadow.cpp