If love has left you with emotional scars, take solace in knowing that things could be worse – just watch Heart Eyes, a freshly released horror movie featuring a murderer who preys on romantic partners during the love-filled season of Valentine’s Day. This storyline might make you appreciate the single life a bit more.
Recently added to Netflix’s catalog, this horror flick from 2025 swiftly climbed the ranks to become one of the most-watched movies, occupying a top spot in Netflix’s Top 10 trending list. It appears that horror enthusiasts on Netflix have a strong appetite for terrifying tales!
Crafted under the direction of Josh Ruben, the creative mind behind the 2021 horror comedy Werewolves Within, and penned by the trio of Phillip Murphy, Christopher Landon from Happy Death Day fame (doubling as a producer), and Michael Kennedy, Heart Eyes offers a unique and somewhat comical twist on the slasher genre, perfectly fitting for the romantic backdrop of Valentine’s Day. Why should Halloween hog all the scares? Let every holiday have its own gruesome antagonist.
If you got confused, or if you simply don’t have the patience to watch Heart Eyes to the end, Decider is here to help. Read on for a breakdown of the Heart Eyes ending explained, including who is the Heart Eyes killer.
Ally McCabe (Olivia Holt) no longer believes in love after her being dumped by her ex-boyfriend. She spearheads a dark advertising campaign for the jewelry company where she works, featuring lovers dying. This ends up being very poor timing, because the “Heart Eyes killer”—a notorious masked serial killer who targets couples on Valentine’s Day—has come to their city.
A cute consultant named Jay Simmons (Mason Gooding) gets hired by Ally’s company in order to fix her mistake. They get dinner together to talk business, and Jay tries to flirt, but Ally shuts him down. On the way out of the restaurant, Ally sees her ex, and impulsively kisses Jay to make her ex jealous. When she gets home and realizes she’s locked out of her apartment, Jay breaks the window for her, cutting his hand in the process. Ally patches up Jay’s hand and reveals to him that she went to med school, but dropped out because she’s squeamish around blood.
Ally and Jay are suddenly attacked by the Heart Eyes killer, who was hiding in Ally’s bedroom closet. In the struggle that ensues, Ally runs for her life, leaving Jay behind. Ally eventually finds two cops, Detectives Jeanine Shaw and Zeke Hobbs. The cops reveal that they have arrested Jay as the Heart Eyes killer, after finding him with the killer’s mask and weapon. In addition, his initials match a wedding ring found at a crime scene with the inscription “JS.”
Ally follows the cops to the police station, insisting that Jay is innocent. Detective Jeanine Shaw flirts with Jay in the interrogation room. While waiting at the police station, Ally meets an IT guy named David, who politely asks her out. She declines, citing the fact that someone just tried to murder her. Soon after, the killer attacks the police station. This time, Ally doesn’t leave Jay behind.
Ally and Jay flee to a drive-in movie theater full of couples, with the killer hot on their tails. Ally feels bad when all those couples start getting murdered, and she convinces Jay to help her confront the killer. They do, and in the fight that ensues, Ally and Jay manage to stab and kill the Heart Eyes Killer. They remove his mask, and discover a man they’ve never seen before. Assuming it’s all over, Jay invites Ally back to his place. But she’s still afraid to love, so she turns him down. That flirty cop, Jeanine Shaw, offers Jay a ride home, and he accepts.
Ally realizes she’s in love with Jay and regrets letting him go. After some encouragement from her best friend Monica (Gigi Zumbado), Ally rushes to find Jay to tell him how she feels. Then she gets a call from Heart Eyes who reveals that Jay has been kidnapped, and Ally needs to show up alone if she wants to save him. Ally goes, and when she gets there, we learn the true identity of the Heart Eyes killer.
There are three Heart Eyes killers: Jeanine Shaw the cop, David the IT guy, and a third guy named Eli. The guy that Ally and Jay killed was Eli, who David describes as a “fanboy.” Jeanine and David, a couple, are the main two killers. The “JS” wedding ring was David’s, for his wife, “Jeanine Shaw.” Killing people is, apparently, their shared kink. The killers went to the restaurant intending to kill Ally’s ex and his new girlfriend, but then they say Ally and Jay kiss. They were intrigued by their chemistry, and made them the next target.
Jeanine wants to know if Ally really fell for Jay. She gives Ally a gun, and tells Ally to either shoot Jay, or shoot herself. Ally pretends to be gearing up to shoot herself, but then she shoots Jeanine instead. However, because she’s such a bad shot, she also accidentally hits Jay. Luckily, he falls into some lit candles, and the melted wax helps close up his gunshot wound. (Sure.)
In the bloody fight that follows, Ally and Jay manage to kill both Jeanine and David. Yay! The Heart Eyes Killers are dead. And Ally finally got over her thing about blood.
One year later, Ally and Jay are dating. Ally leaves her job to go back to medical school. Jay asks Ally to move in together. Ally tell Jay she doesn’t want a key… she wants a proposal. Ally asks Jay to marry her, proving that she is no longer afraid of love.
With that, the movie ends. But wait, there’s more.
Yes, there is a Heart Eyes mid-credits scene. Immediately after the proposal, Ally gets a phone call that sounds like the killer. But it’s quickly revealed it is just Ally’s friend Monica pranking her. Pretty insensitive, Monica!
Monica laughs and congratulates Ally on her engagement… but the call cuts off abruptly. Did something happen to Ally on the other end? Is it the killer? Or did she just lose service?
The movie leaves that up to you to decide.
(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src = “//connect.facebook.net/en_US/sdk.js#xfbml=1&appId=823934954307605&version=v2.8”;
fjs.parentNode.insertBefore(js, fjs);
}(document, ‘script’, ‘facebook-jssdk’));