diff --git a/Telegram/SourceFiles/media/player/media_player_button.cpp b/Telegram/SourceFiles/media/player/media_player_button.cpp
index e56f39060..eaffe8bff 100644
--- a/Telegram/SourceFiles/media/player/media_player_button.cpp
+++ b/Telegram/SourceFiles/media/player/media_player_button.cpp
@@ -29,7 +29,7 @@ template <int N>
 QPainterPath interpolatePaths(QPointF (&from)[N], QPointF (&to)[N], float64 k) {
 	static_assert(N > 1, "Wrong points count in path!");
 
-	auto from_coef = k, to_coef = 1. - k;
+	auto from_coef = 1. - k, to_coef = k;
 	QPainterPath result;
 	auto x = from[0].x() * from_coef + to[0].x() * to_coef;
 	auto y = from[0].y() * from_coef + to[0].y() * to_coef;
@@ -43,11 +43,8 @@ QPainterPath interpolatePaths(QPointF (&from)[N], QPointF (&to)[N], float64 k) {
 
 } // namespace
 
-PlayButtonLayout::PlayButtonLayout(const style::MediaPlayerButton &st, State state, UpdateCallback &&callback)
+PlayButtonLayout::PlayButtonLayout(const style::MediaPlayerButton &st, UpdateCallback &&callback)
 : _st(st)
-, _state(state)
-, _oldState(state)
-, _nextState(state)
 , _callback(std_::move(callback)) {
 }
 
@@ -59,7 +56,10 @@ void PlayButtonLayout::setState(State state) {
 		_oldState = _state;
 		_state = _nextState;
 		_transformBackward = false;
-		if (_state != _oldState) startTransform(0., 1.);
+		if (_state != _oldState) {
+			startTransform(0., 1.);
+			if (_callback) _callback();
+		}
 	} else if (_oldState == _nextState) {
 		qSwap(_oldState, _state);
 		startTransform(_transformBackward ? 0. : 1., _transformBackward ? 1. : 0.);
@@ -67,6 +67,12 @@ void PlayButtonLayout::setState(State state) {
 	}
 }
 
+void PlayButtonLayout::finishTransform() {
+	_transformProgress.finish();
+	_transformBackward = false;
+	if (_callback) _callback();
+}
+
 void PlayButtonLayout::paint(Painter &p, const QBrush &brush) {
 	if (_transformProgress.animating(getms())) {
 		auto from = _oldState, to = _state;
@@ -145,7 +151,7 @@ void PlayButtonLayout::paintPlayToPause(Painter &p, const QBrush &brush, float64
 		{ playLeft + (playWidth / 2.), playTop + (3 * playHeight / 4.) },
 		{ playLeft, playTop + playHeight },
 	};
-	p.fillPath(interpolatePaths(pathLeftPause, pathLeftPlay, progress), brush);
+	p.fillPath(interpolatePaths(pathLeftPlay, pathLeftPause, progress), brush);
 
 	QPointF pathRightPause[] = {
 		{ pauseLeft + pauseWidth - pauseStroke, pauseTop },
@@ -159,17 +165,108 @@ void PlayButtonLayout::paintPlayToPause(Painter &p, const QBrush &brush, float64
 		{ playLeft + playWidth, playTop + (playHeight / 2.) },
 		{ playLeft + (playWidth / 2.), playTop + (3 * playHeight / 4.) },
 	};
-	p.fillPath(interpolatePaths(pathRightPause, pathRightPlay, progress), brush);
+	p.fillPath(interpolatePaths(pathRightPlay, pathRightPause, progress), brush);
 
 	p.setRenderHint(QPainter::HighQualityAntialiasing, false);
 }
 
 void PlayButtonLayout::paintPlayToCancel(Painter &p, const QBrush &brush, float64 progress) {
+	static const auto sqrt2 = sqrt(2.);
 
+	auto playLeft = 0. + _st.playPosition.x();
+	auto playTop = 0. + _st.playPosition.y();
+	auto playWidth = _st.playOuter.width() - 2 * playLeft;
+	auto playHeight = _st.playOuter.height() - 2 * playTop;
+
+	auto cancelLeft = 0. + _st.cancelPosition.x();
+	auto cancelTop = 0. + _st.cancelPosition.y();
+	auto cancelWidth = _st.cancelOuter.width() - 2 * cancelLeft;
+	auto cancelHeight = _st.cancelOuter.height() - 2 * cancelTop;
+	auto cancelStroke = (0. + _st.cancelStroke) / sqrt2;
+
+	p.setPen(Qt::NoPen);
+	p.setRenderHint(QPainter::HighQualityAntialiasing, true);
+
+	QPointF pathPlay[] = {
+		{ playLeft, playTop },
+		{ playLeft, playTop },
+		{ playLeft + (playWidth / 2.), playTop + (playHeight / 4.) },
+		{ playLeft + playWidth, playTop + (playHeight / 2.) },
+		{ playLeft + playWidth, playTop + (playHeight / 2.) },
+		{ playLeft + playWidth, playTop + (playHeight / 2.) },
+		{ playLeft + playWidth, playTop + (playHeight / 2.) },
+		{ playLeft + playWidth, playTop + (playHeight / 2.) },
+		{ playLeft + (playWidth / 2.), playTop + (3 * playHeight / 4.) },
+		{ playLeft, playTop + playHeight },
+		{ playLeft, playTop + playHeight },
+		{ playLeft, playTop + (playHeight / 2.) },
+	};
+	QPointF pathCancel[] = {
+		{ cancelLeft, cancelTop + cancelStroke },
+		{ cancelLeft + cancelStroke, cancelTop },
+		{ cancelLeft + (cancelWidth / 2.), cancelTop + (cancelHeight / 2.) - cancelStroke },
+		{ cancelLeft + cancelWidth - cancelStroke, cancelTop },
+		{ cancelLeft + cancelWidth, cancelTop + cancelStroke },
+		{ cancelLeft + (cancelWidth / 2.) + cancelStroke, cancelTop + (cancelHeight / 2.) },
+		{ cancelLeft + cancelWidth, cancelTop + cancelHeight - cancelStroke },
+		{ cancelLeft + cancelWidth - cancelStroke, cancelTop + cancelHeight },
+		{ cancelLeft + (cancelWidth / 2.), cancelTop + (cancelHeight / 2.) + cancelStroke },
+		{ cancelLeft + cancelStroke, cancelTop + cancelHeight },
+		{ cancelLeft, cancelTop + cancelHeight - cancelStroke },
+		{ cancelLeft + (cancelWidth / 2.) - cancelStroke, cancelTop + (cancelHeight / 2.) },
+	};
+	p.fillPath(interpolatePaths(pathPlay, pathCancel, progress), brush);
+
+	p.setRenderHint(QPainter::HighQualityAntialiasing, false);
 }
 
 void PlayButtonLayout::paintPauseToCancel(Painter &p, const QBrush &brush, float64 progress) {
+	static const auto sqrt2 = sqrt(2.);
 
+	auto pauseLeft = 0. + _st.pausePosition.x();
+	auto pauseTop = 0. + _st.pausePosition.y();
+	auto pauseWidth = _st.pauseOuter.width() - 2 * pauseLeft;
+	auto pauseHeight = _st.pauseOuter.height() - 2 * pauseTop;
+	auto pauseStroke = 0. + _st.pauseStroke;
+
+	auto cancelLeft = 0. + _st.cancelPosition.x();
+	auto cancelTop = 0. + _st.cancelPosition.y();
+	auto cancelWidth = _st.cancelOuter.width() - 2 * cancelLeft;
+	auto cancelHeight = _st.cancelOuter.height() - 2 * cancelTop;
+	auto cancelStroke = (0. + _st.cancelStroke) / sqrt2;
+
+	p.setPen(Qt::NoPen);
+	p.setRenderHint(QPainter::HighQualityAntialiasing, true);
+
+	QPointF pathLeftPause[] = {
+		{ pauseLeft, pauseTop },
+		{ pauseLeft + pauseStroke, pauseTop },
+		{ pauseLeft + pauseStroke, pauseTop + pauseHeight },
+		{ pauseLeft, pauseTop + pauseHeight },
+	};
+	QPointF pathLeftCancel[] = {
+		{ cancelLeft, cancelTop + cancelStroke },
+		{ cancelLeft + cancelStroke, cancelTop },
+		{ cancelLeft + cancelWidth, cancelTop + cancelHeight - cancelStroke },
+		{ cancelLeft + cancelWidth - cancelStroke, cancelTop + cancelHeight },
+	};
+	p.fillPath(interpolatePaths(pathLeftPause, pathLeftCancel, progress), brush);
+
+	QPointF pathRightPause[] = {
+		{ pauseLeft + pauseWidth - pauseStroke, pauseTop },
+		{ pauseLeft + pauseWidth, pauseTop },
+		{ pauseLeft + pauseWidth, pauseTop + pauseHeight },
+		{ pauseLeft + pauseWidth - pauseStroke, pauseTop + pauseHeight },
+	};
+	QPointF pathRightCancel[] = {
+		{ cancelLeft + cancelWidth - cancelStroke, cancelTop },
+		{ cancelLeft + cancelWidth, cancelTop + cancelStroke },
+		{ cancelLeft + cancelStroke, cancelTop + cancelHeight },
+		{ cancelLeft, cancelTop + cancelHeight - cancelStroke },
+	};
+	p.fillPath(interpolatePaths(pathRightPause, pathRightCancel, progress), brush);
+
+	p.setRenderHint(QPainter::HighQualityAntialiasing, false);
 }
 
 void PlayButtonLayout::animationCallback() {
diff --git a/Telegram/SourceFiles/media/player/media_player_button.h b/Telegram/SourceFiles/media/player/media_player_button.h
index f91f680a3..1335e55b8 100644
--- a/Telegram/SourceFiles/media/player/media_player_button.h
+++ b/Telegram/SourceFiles/media/player/media_player_button.h
@@ -34,9 +34,10 @@ public:
 		Cancel,
 	};
 	using UpdateCallback = FloatAnimation::Callback;
-	PlayButtonLayout(const style::MediaPlayerButton &st, State state, UpdateCallback &&callback);
+	PlayButtonLayout(const style::MediaPlayerButton &st, UpdateCallback &&callback);
 
 	void setState(State state);
+	void finishTransform();
 	void paint(Painter &p, const QBrush &brush);
 
 private:
@@ -50,7 +51,9 @@ private:
 
 	const style::MediaPlayerButton &_st;
 
-	State _state, _oldState, _nextState;
+	State _state = State::Play;
+	State _oldState = State::Play;
+	State _nextState = State::Play;
 	FloatAnimation _transformProgress;
 	bool _transformBackward = false;
 
diff --git a/Telegram/SourceFiles/media/player/media_player_cover.cpp b/Telegram/SourceFiles/media/player/media_player_cover.cpp
index 62e02dcf4..dd8cd4df4 100644
--- a/Telegram/SourceFiles/media/player/media_player_cover.cpp
+++ b/Telegram/SourceFiles/media/player/media_player_cover.cpp
@@ -36,12 +36,18 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 namespace Media {
 namespace Player {
 
+using State = PlayButtonLayout::State;
+
 class CoverWidget::PlayButton : public Button {
 public:
 	PlayButton(QWidget *parent);
 
-	using State = PlayButtonLayout::State;
-	void setState(State state);
+	void setState(State state) {
+		_layout.setState(state);
+	}
+	void finishTransform() {
+		_layout.finishTransform();
+	}
 
 protected:
 	void paintEvent(QPaintEvent *e) override;
@@ -52,12 +58,9 @@ private:
 };
 
 CoverWidget::PlayButton::PlayButton(QWidget *parent) : Button(parent)
-, _layout(st::mediaPlayerButton, State::Pause, [this] { update(); }) {
+, _layout(st::mediaPlayerButton, [this] { update(); }) {
 	resize(st::mediaPlayerButtonSize);
-}
-
-void CoverWidget::PlayButton::setState(State state) {
-	_layout.setState(state);
+	setCursor(style::cur_pointer);
 }
 
 void CoverWidget::PlayButton::paintEvent(QPaintEvent *e) {
@@ -82,15 +85,9 @@ CoverWidget::CoverWidget(QWidget *parent) : TWidget(parent)
 	_playback->setChangeFinishedCallback([this](float64 value) {
 		handleSeekFinished(value);
 	});
-
-	_playPause->setState(_showPause ? PlayButton::State::Pause : PlayButton::State::Play);
 	_playPause->setClickedCallback([this]() {
 		if (exists()) {
-			if (_showPause) {
-				instance()->pause();
-			} else {
-				instance()->play();
-			}
+			instance()->playPauseCancelClicked();
 		}
 	});
 
@@ -115,6 +112,7 @@ CoverWidget::CoverWidget(QWidget *parent) : TWidget(parent)
 			AudioMsgId playing;
 			auto playbackState = player->currentState(&playing, AudioMsgId::Type::Song);
 			handleSongUpdate(UpdatedEvent(&playing, &playbackState));
+			_playPause->finishTransform();
 		}
 	}
 }
@@ -201,10 +199,15 @@ void CoverWidget::handleSongUpdate(const UpdatedEvent &e) {
 	if (exists() && instance()->isSeeking()) {
 		showPause = true;
 	}
-	if (_showPause != showPause) {
-		_showPause = showPause;
-		_playPause->setState(showPause ? PlayButton::State::Pause : PlayButton::State::Play);
-	}
+	auto state = [audio = audioId.audio(), showPause] {
+		if (audio->loading()) {
+			return State::Cancel;
+		} else if (showPause) {
+			return State::Pause;
+		}
+		return State::Play;
+	};
+	_playPause->setState(state());
 
 	updateTimeText(audioId, playbackState);
 }
@@ -222,15 +225,15 @@ void CoverWidget::updateTimeText(const AudioMsgId &audioId, const AudioPlaybackS
 
 	_lastDurationMs = (playbackState.duration * 1000LL) / frequency;
 
-	if (duration || !audioId.audio()->loading()) {
-		display = display / frequency;
-		_time = formatDurationText(display);
-		_playback->setDisabled(false);
-	} else {
+	if (audioId.audio()->loading()) {
 		auto loaded = audioId.audio()->loadOffset();
 		auto loadProgress = snap(float64(loaded) / qMax(audioId.audio()->size, 1), 0., 1.);
 		_time = QString::number(qRound(loadProgress * 100)) + '%';
 		_playback->setDisabled(true);
+	} else {
+		display = display / frequency;
+		_time = formatDurationText(display);
+		_playback->setDisabled(false);
 	}
 	if (_seekPositionMs < 0) {
 		updateTimeLabel();
diff --git a/Telegram/SourceFiles/media/player/media_player_cover.h b/Telegram/SourceFiles/media/player/media_player_cover.h
index 9da7bd306..333c106da 100644
--- a/Telegram/SourceFiles/media/player/media_player_cover.h
+++ b/Telegram/SourceFiles/media/player/media_player_cover.h
@@ -64,7 +64,6 @@ private:
 	void updateTimeText(const AudioMsgId &audioId, const AudioPlaybackState &playbackState);
 	void updateTimeLabel();
 
-	bool _showPause = true;
 	int64 _seekPositionMs = -1;
 	int64 _lastDurationMs = 0;
 	QString _time;
diff --git a/Telegram/SourceFiles/media/player/media_player_instance.cpp b/Telegram/SourceFiles/media/player/media_player_instance.cpp
index 3dbea71ac..8bed47180 100644
--- a/Telegram/SourceFiles/media/player/media_player_instance.cpp
+++ b/Telegram/SourceFiles/media/player/media_player_instance.cpp
@@ -209,6 +209,25 @@ void Instance::previous() {
 	moveInPlaylist(-1);
 }
 
+void Instance::playPauseCancelClicked() {
+	if (isSeeking()) {
+		return;
+	}
+
+	AudioMsgId playing;
+	auto playbackState = audioPlayer()->currentState(&playing, AudioMsgId::Type::Song);
+	auto stopped = ((playbackState.state & AudioPlayerStoppedMask) || playbackState.state == AudioPlayerFinishing);
+	auto showPause = !stopped && (playbackState.state == AudioPlayerPlaying || playbackState.state == AudioPlayerResuming || playbackState.state == AudioPlayerStarting);
+	auto audio = playing.audio();
+	if (audio && audio->loading()) {
+		audio->cancel();
+	} else if (showPause) {
+		pause();
+	} else {
+		play();
+	}
+}
+
 void Instance::startSeeking() {
 	_seeking = _current;
 	pause();
diff --git a/Telegram/SourceFiles/media/player/media_player_instance.h b/Telegram/SourceFiles/media/player/media_player_instance.h
index 48ef5f358..4ea505d88 100644
--- a/Telegram/SourceFiles/media/player/media_player_instance.h
+++ b/Telegram/SourceFiles/media/player/media_player_instance.h
@@ -62,6 +62,8 @@ public:
 	void next();
 	void previous();
 
+	void playPauseCancelClicked();
+
 	void play(const AudioMsgId &audioId);
 	const AudioMsgId &current() const {
 		return _current;
diff --git a/Telegram/SourceFiles/media/player/media_player_title_button.cpp b/Telegram/SourceFiles/media/player/media_player_title_button.cpp
index 65531ec05..12b500adc 100644
--- a/Telegram/SourceFiles/media/player/media_player_title_button.cpp
+++ b/Telegram/SourceFiles/media/player/media_player_title_button.cpp
@@ -32,17 +32,13 @@ namespace Player {
 using State = PlayButtonLayout::State;
 
 TitleButton::TitleButton(QWidget *parent) : Button(parent)
-, _layout(std_::make_unique<PlayButtonLayout>(st::mediaPlayerTitleButton, State::Pause, [this] { update(); })) {
+, _layout(std_::make_unique<PlayButtonLayout>(st::mediaPlayerTitleButton, [this] { update(); })) {
 	setAttribute(Qt::WA_OpaquePaintEvent);
 	resize(st::mediaPlayerTitleButtonSize);
 
 	setClickedCallback([this]() {
 		if (exists()) {
-			if (_showPause) {
-				instance()->pause();
-			} else {
-				instance()->play();
-			}
+			instance()->playPauseCancelClicked();
 		}
 	});
 
@@ -51,6 +47,7 @@ TitleButton::TitleButton(QWidget *parent) : Button(parent)
 			updatePauseState();
 		});
 		updatePauseState();
+		_layout->finishTransform();
 	}
 }
 
@@ -62,15 +59,15 @@ void TitleButton::updatePauseState() {
 	if (exists() && instance()->isSeeking()) {
 		showPause = true;
 	}
-	setShowPause(showPause);
-}
-
-void TitleButton::setShowPause(bool showPause) {
-	if (_showPause != showPause) {
-		_showPause = showPause;
-		_layout->setState(_showPause ? PlayButtonLayout::State::Pause : PlayButtonLayout::State::Play);
-		update();
-	}
+	auto state = [audio = playing.audio(), showPause] {
+		if (audio && audio->loading()) {
+			return State::Cancel;
+		} else if (showPause) {
+			return State::Pause;
+		}
+		return State::Play;
+	};
+	_layout->setState(state());
 }
 
 void TitleButton::paintEvent(QPaintEvent *e) {
diff --git a/Telegram/SourceFiles/media/player/media_player_title_button.h b/Telegram/SourceFiles/media/player/media_player_title_button.h
index 5ed69e6f0..a240e95d8 100644
--- a/Telegram/SourceFiles/media/player/media_player_title_button.h
+++ b/Telegram/SourceFiles/media/player/media_player_title_button.h
@@ -42,9 +42,7 @@ protected:
 
 private:
 	void paintIcon(Painter &p);
-	void setShowPause(bool showPause);
 
-	bool _showPause = true;
 	std_::unique_ptr<PlayButtonLayout> _layout;
 	ColorAnimation _iconFg;